/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 combat_actor.cpp

	$Header: /heroes4/combat_creature.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_creature.h"

#include <assert.h>
#include <math.h>

#include "adaptor_handler.h"
#include "angle.h"
#include "animated_window.h"
#include "animation.h"
#include "artifact_prop_spell_charges.h"
#include "artifact_slot.h"
#include "battlefield.h"
#include "battlefield_window.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "check_artifact_influence.h"
#include "combat_actor_action.h"
#include "combat_actor_model.h"
#include "combat_creature_ai_data.h"
#include "combat_label.h"
#include "combat_object_model_cache.h"
#include "combat_object_type.h"
#include "combat_object_visitor.h"
#include "combat_reader.h"
#include "combat_sounds.h"
#include "combat_spell.h"
#include "combat_spell_registration.h"
#include "combat_window.h"
#include "creature.h"
#include "creature_array.h"
#include "creature_stack.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "delay_effect.h"
#include "difficulty_level.h"
#include "direction.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "game_time.h"
#include "get_artifact_damage_modifier.h"
#include "hero.h"
#include "martyr.h"
#include "missile_type.h"
#include "music.h"
#include "obstacle_type.h"
#include "pixel_24.h"
#include "play_combat_animation.h"
#include "random.h"
#include "replace_keywords.h"
#include "screen_point.h"
#include "sound.h"
#include "spell_effect_window.h"
#include "spell_properties.h"
#include "stationary_combat_object.h"
#include "terrain.h"
#include "threat_footprint.h"
#include "town_image_level.h"
#include "town_type.h"

extern t_external_string const k_combat_action_cause_effect;

// ------------------------------------------------------------------------------
// pain mirror action
// ------------------------------------------------------------------------------
namespace
{
	class t_pain_mirror_action : public t_delayed_effect
	{
	public:
		t_pain_mirror_action( t_combat_creature_ptr caster, t_combat_creature_ptr target,
			                  int damage, bool permanent );
		virtual void execute( t_window* window );
		virtual void operator()();
	protected:
		t_battlefield&			m_battlefield;
		t_combat_creature_ptr	m_caster;
		t_combat_creature_ptr	m_target;
		int						m_damage;
		bool					m_permanent;
	};
};

// ----------------------------------------------------------------------
// initialize spell props
// ----------------------------------------------------------------------
t_combat_creature::t_spell_effect::t_spell_effect()
{
	power = 0;
	active = false;
	memset( source, 0, sizeof( source ));
	duration = 0;
}

t_combat_creature_ai_data::t_combat_creature_ai_data()
{
	do_not_wait = false;
	lifespan = -1;
	clear();
}

void t_combat_creature_ai_data::clear()
{
	int i;

	closest_melee_target = 0;
	closest_melee_target_valid = false;
	value_per_hit = -1.0;
	ranged_value_per_hit = -1.0;
	melee_value_per_hit = -1.0;
	spellcasting_value_per_hit = -1.0;
	tactics_value_per_hit = -1.0;
	for (i = 0; i < 2; ++i)
		path_finder[i].reset();
	for (i = 0; i < 2; ++i)
	{
		damage_done_limit[i] = -1;
		damage_taken_limit[i] = -1;
		threat_type_weight[i] = -1.0;
	}
}


// ------------------------------------------------------------------------------
// t_combat_creature members
// ------------------------------------------------------------------------------
t_combat_creature::t_combat_creature( t_battlefield& battlefield )
				 : m_battlefield( battlefield ), t_combat_actor( &battlefield )
{
	initialize();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_combat_creature::t_combat_creature( t_battlefield&			battlefield,
									  t_creature_stack*			stack, 
									  int						original_slot,
									  t_creature_array const*	army,
									  double					scale,
									  bool						defender,
									  t_player const*			owner )
	:	t_combat_actor( &battlefield, stack->get_combat_model( scale ), k_combat_actor_action_wait,
	                    k_direction_northeast ),
		t_abstract_combat_creature( stack, original_slot, defender, owner ),
		m_battlefield( battlefield )
{
	initialize();
	m_wounds = stack->get_wounds();
	m_defense_bonus[false] = stack->get_defense_bonus( false );
	m_defense_bonus[true] = stack->get_defense_bonus( true );

	transfer_spells( stack );

	m_morale = stack->get_morale();
	if (army != 0)
		m_morale += army->get_morale( stack->get_alignment() );

	// set abilities
	t_creature_ability ability;

	for (ability = t_creature_ability(0); ability < k_ability_count; enum_incr(ability))
	{
		m_has_ability[ability] = stack->has_ability( ability );
		m_normally_has_ability[ability] = m_has_ability[ability];
	}

	if (has_ability( k_ability_stone_skin ))
	{
		set_spell( k_spell_stoneskin, t_combat_action_message(), 0, k_spell_source_permanent );
	}
	if (stack->has_ability( k_ability_block ))
		m_block_chance = 30;

	check_artifacts( *this );
	set_sounds();

	// determine default spell
	t_spell spell;
	t_creature_type creature_type = stack->get_creature_type();

	if (has_ability( k_ability_ranged ))
		m_preferred_action = k_combat_mode_ranged;
	else if (has_ability( k_ability_strike_and_return ))
		m_preferred_action = k_combat_mode_strike_and_return;
	else
		m_preferred_action = k_combat_mode_melee;

	m_default_spell = k_spell_none;
	if (creature_type != k_creature_none)
	{
		m_default_spell = ::get_default_spell( creature_type );
		if (m_default_spell != k_spell_none && !is_curse( m_default_spell ))
			m_preferred_action = k_combat_mode_spell;
	}
	else
	{
		for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
		{
			if (is_single_target( spell ) && knows_spell( spell )
				&& get_spell_cost( spell ) <= m_spell_points)
			{
				if (m_default_spell != k_spell_none)
				{
					if (is_damage( m_default_spell ) != is_damage(spell))
					{
						if (is_damage( m_default_spell ))
							continue;
					}
					else
					{
						if (get_spell_level( spell ) <= get_spell_level( m_default_spell))
							continue;
					}
				}
				m_default_spell = spell;
			}
		}
	}

	t_combat_label_ptr label;
	t_player_color     color = m_battlefield.get_player_color( defender );

	if (has_action( k_combat_actor_action_base_frame ))
		set_current_action( k_combat_actor_action_base_frame );
	else
		set_current_action( k_combat_actor_action_wait );
	if (defender)
		set_current_direction( k_direction_southwest );
	else
		set_current_direction( k_direction_northeast );
	label = new t_combat_label( *this, color, scale );
	check_morale(); // set morale for this creature.
}

void t_combat_creature::initialize()
{
	m_ai_data.reset( new t_combat_creature_ai_data );
	m_artifact_move_bonus = 0;
	m_artifact_spell_cost_modifier = 0;
	m_blocking_attack = false;
	m_block_chance = 0;
	m_defending = false;
	m_got_bad_luck = false;
	m_got_bad_morale = false;
	m_got_good_luck = false;
	m_got_good_morale = false;
	m_guardian_robe_strength = 0;
	m_has_leeched = false;
	m_has_regenerated = false;
	m_has_retaliated = false;
	m_in_tower = false;
	m_in_water = false;
	m_cheating_state = k_not_cheating;
	m_is_underlay = false;
	m_magic_resistance_bonus = 0;
	m_morale_shown = false;
	m_move_bonus = 0;
	m_new_damage = 0;
	m_new_damage_is_permanent = false;
	m_new_poison = 0;
	m_normal_alpha = 15;
	m_offense_bonus = 0;
	m_permanent_deaths = 0;
	m_plague_checked = false;
	m_poison_checked = false;
	m_robe_blocked_damage = false;
	m_selected = false;
	m_waiting = false;
	m_influence_set = false;
	m_tactics_speed_bonus = 0;
	m_terrain_defense = 0;
}

// --------------------------------------------------------------------------
// initialize the sounds
// --------------------------------------------------------------------------
void t_combat_creature::set_sounds()
{
	t_combat_actor_action_id action;

	for (action = t_combat_actor_action_id(0); action < k_combat_actor_action_count;
	     enum_incr(action))
	{
		m_sounds[action] = get_combat_sound( *m_creature_stack, action );
	}
}


int t_combat_creature::get_spell_cost( t_spell spell ) const
{
	int base = ::get_spell_cost( spell );
	int result = m_creature_stack->get_spell_cost( spell );

	if (is_active( k_spell_power_drain ))
		result += base;
	if (is_active( k_spell_magic_leech ))
		result += base;
	if (is_active( k_spell_mana_flare ))
		result -= 2;
	if (m_artifact_spell_cost_modifier < 0)
		result = result * (100 + m_artifact_spell_cost_modifier) / 100;
	if (result < 1)
		result = 1;
	return result;
}

t_abstract_grail_data_source const& t_combat_creature::get_grail_data() const
{
	return m_battlefield.get_grail_data( m_belongs_to_defender );
}

// ------------------------------------------------------------------------------
// reset the ai variables
// ------------------------------------------------------------------------------
void t_combat_creature::clear_ai_variables()
{
	m_ai_data->clear();
}

// ------------------------------------------------------------------------------
// get closest melee target
// ------------------------------------------------------------------------------
t_combat_creature* t_combat_creature::get_closest_melee_target() const
{
	if (!m_ai_data->closest_melee_target_valid)
	{
		t_battlefield&						battlefield = m_battlefield;
		t_ai_combat_data_cache&				data = battlefield.get_data_cache();
		t_combat_creature_list::iterator	index = battlefield.creatures_begin();
		t_combat_creature_list::iterator	end = battlefield.creatures_end();
		t_combat_creature*					target;
		int									turns;
		int									closest;

		m_ai_data->closest_melee_target = 0;
		for (; index != end; ++index)
		{
			target = *index;
			if (target->belongs_to_defender() == get_controller())
				continue;
			if (target->get_number() == 0)
				continue;
			if (!data.get_attack_time( *this, *target, turns ))
				continue;
			if (m_ai_data->closest_melee_target != 0 && closest <= turns)
				continue;
			closest = turns;
			m_ai_data->closest_melee_target = target;
			if (turns == 1)
				break;
		}
		m_ai_data->closest_melee_target_valid = true;
	}
	return m_ai_data->closest_melee_target;
}


// ---------------------------------------------------------------
// compute the value of damage spell
// ---------------------------------------------------------------
static double get_damage_spell_value( t_battlefield& battlefield, bool side, t_spell spell,
									  int damage )
{
	double								result = 0.0;
	double								value;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			target;
	double								total_hits;

	for (; index != end; ++index)
	{
		target = *index;
		if (target->get_controller() != side)
			continue;
		if (target->get_number() == 0)
			continue;
		if (!can_affect( spell, *target ))
			continue;
		value = damage;
		total_hits = target->get_total_hits();
		if (value > total_hits)
			value = total_hits;
		value = value * target->get_defense_basic( true ) * target->get_defense_bonus( true );
		if (result < value)
			result = value;
	}
	return result;
}

static double get_basic_damage_multiplier( t_combat_creature const& creature, bool ranged )
{
	return creature.get_number() * creature.get_offense( ranged ) 
		* (creature.get_attacks( ranged ) + 1) * 0.5;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_ranged_reduction() const
{
	t_battlefield&	battlefield = m_battlefield;

	if (!battlefield.can_shoot( *this ))
		return 0.0;

	// find the target which we will do the most damage to.
	double								best_factor = 0.0;
	double								defense_factor;
	bool								first = true;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					target;
	double								range_factor;

	for (; index != end; ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target->belongs_to_defender() == get_controller())
			continue;
		range_factor = battlefield.get_range_factor( *this, *target );
		if (range_factor == 0.0)
			continue;
		// adjust for differences between ranged and melee defense
		defense_factor = target->get_defense_basic( false ) * target->get_defense_bonus( false ) 
					 / (target->get_defense_basic( true ) * target->get_defense_bonus( true ));
		if (defense_factor > 1.0)
			range_factor *= defense_factor;
		if (!first && range_factor >= best_factor)
			continue;
		first = false;
		best_factor = range_factor;
		if (best_factor <= 1.0)
			break;
	}
	if (first)
		return 0.0;

	return 1.0 / best_factor;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_melee_reduction() const
{
	t_combat_creature*		target;
	int						turns;

	target = get_closest_melee_target();
	if (target == 0)
		turns = 5;
	else
	{
		t_battlefield&			battlefield = m_battlefield;
		t_ai_combat_data_cache&	data = battlefield.get_data_cache();

		data.get_attack_time( *this, *target, turns );
	}
	return 1.0 / turns;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_melee_value_per_hit() const
{
	if (m_ai_data->melee_value_per_hit < 0.0)
	{
		// compute value of melee attack
		double damage;

		damage = (get_damage_low() + get_damage_high()) * 0.5;
		m_ai_data->melee_value_per_hit = damage * get_offense( false )
									  * (get_attacks( false ) + 1) * 0.5 
									  * get_ai_melee_reduction() / get_hit_points();
		if (is_summoned())
			m_ai_data->melee_value_per_hit *= 0.5;
			// at easy, we try and ignore player heroes.
		if (get_hero() != 0 && m_battlefield.get_difficulty() == k_difficulty_easy
			&& !m_battlefield.is_computer_controlled( get_controller() ))
		{
			m_ai_data->melee_value_per_hit *= 0.5;
		}
	}
	return m_ai_data->melee_value_per_hit;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_ranged_value_per_hit() const
{
	if (m_ai_data->ranged_value_per_hit < 0.0)
	{
		t_battlefield&			battlefield = m_battlefield;
		
		// compute value of melee attack
		if (battlefield.can_shoot( *this ))
		{
			double damage = (get_damage_low() + get_damage_high()) * 0.5;

			m_ai_data->ranged_value_per_hit = damage * get_offense( true ) 
											* (get_attacks( true ) + 1) * 0.5 
											* get_ai_ranged_reduction() / get_hit_points();
			if (is_summoned())
				m_ai_data->ranged_value_per_hit *= 0.5;
			// at easy, we try and ignore hero archers.
			if (get_hero() != 0 && m_battlefield.get_difficulty() == k_difficulty_easy
				&& !m_battlefield.is_computer_controlled( get_controller() ))
			{
				m_ai_data->melee_value_per_hit *= 0.5;
			}
		}
		else
			m_ai_data->ranged_value_per_hit = 0.0;
	}
	return m_ai_data->ranged_value_per_hit;
}

// ---------------------------------------------------------------
// estimate value of a spell
// ---------------------------------------------------------------
static double get_estimated_enchantment_value( t_battlefield& battlefield, bool side, t_spell spell )
{
	double								result = 0.0;
	double								value;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			target;
	
	for (; index != end; ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target->belongs_to_defender() != side)
			continue;
		if (target->is_incapacitated())
			continue;
		if (!can_affect( spell, *target ))
			continue;

		double ranged_value;

		value = target->get_ai_melee_value_per_hit();
		ranged_value = target->get_ai_ranged_value_per_hit();
		if (value < ranged_value)
			value = ranged_value;
		value = value * target->get_total_hits() * get_ai_spell_value( spell ) * 0.005;
		if (affects_offense( spell ) && affects_defense( spell ))
			value *= 2.0;
		if (is_mass_effect( spell ))
			result += value;
		else if (result < value)
			result = value;
	}
	return result;
}

// ---------------------------------------------------------------
// estimate value of a spell
// ---------------------------------------------------------------
static double get_estimated_hand_of_death_value( t_combat_creature const& caster, t_spell spell )
{
	double								result = 0.0;
	double								value;
	t_battlefield&						battlefield = caster.get_battlefield();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			target;
	double								total_hits;
	int									power;
	int									number = caster.get_number();

	if (number < 1)
		number = 1;
	power = caster.get_spell_power( spell, number );
	for (; index != end; ++index)
	{
		target = *index;
		if (target->belongs_to_defender() == caster.get_controller())
			continue;
		if (target->get_number() == 0)
			continue;
		if (!can_affect( spell, *target ))
			continue;
		value = power * target->get_hit_points();
		total_hits = target->get_total_hits();
		if (value > total_hits)
			value = total_hits;
		value = value * target->get_defense_basic( true ) * target->get_defense_bonus( true );
		if (result < value)
			result = value;
	}
	return result;
}

// ---------------------------------------------------------------
// estimate value of a spell
// ---------------------------------------------------------------
double get_estimated_healing_value( t_combat_creature const& caster, t_spell spell, 
									bool limit_to_wounds )
{
	double								result = 0.0;
	double								value;
	t_battlefield&						battlefield = caster.get_battlefield();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			target;
	int									power;
	int									number = caster.get_number();

	if (number < 1)
		number = 1;
	power = caster.get_spell_power( spell, number );
	for (; index != end; ++index)
	{
		target = *index;
		if (target->get_number() == 0)
			continue;
		if (target->belongs_to_defender() != caster.get_controller())
			continue;
		if (!can_affect( spell, *target ))
			continue;

		int healed = target->get_wounds();

		if (!limit_to_wounds)
			healed += (target->get_original_number() - target->get_number()) 
					* target->get_hit_points();
		
		if (healed > power)
			healed = power;

		value = healed * target->get_defense_basic( false ) * target->get_defense_bonus( false );
		if (is_mass_effect( spell ))
			result += value;
		else if (result < value)
			result = value;
	}
	return result;
}

// ---------------------------------------------------------------
// estimate value of a spell
// ---------------------------------------------------------------
double t_combat_creature::get_ai_estimated_value( t_spell spell ) const
{
	int	number = get_number();

	if (number < 1)
		number = 1;

	if (is_damage( spell ))
	{
		int power;

		power = get_spell_power( spell, number ) * get_ai_spell_value( spell );
		return get_damage_spell_value( m_battlefield, !get_controller(), spell, power );
	}
	if (is_summoning( spell ))
	{
		t_creature_type summoned_creature;
		int				power;

		power = get_spell_power( spell, number );
		summoned_creature = ::get_summoned_creature( spell );
		if (summoned_creature == k_creature_none)
			return get_damage_spell_value( m_battlefield, !get_controller(), spell, power );

		t_creature_traits const& traits = get_traits( summoned_creature );

		return (traits.damage_low + traits.damage_high) * 0.5 * traits.attack_skill * power
			   * 3.0;
	}

	t_ai_spell_type	spell_type;

	spell_type = get_ai_spell_type( spell );
	switch (spell_type)
	{
		case k_ai_spell_type_blessing:
			return get_estimated_enchantment_value( m_battlefield, get_controller(), spell );

		case k_ai_spell_type_curse:
			return get_estimated_enchantment_value( m_battlefield, !get_controller(), spell );

		case k_ai_spell_type_hand_of_death:
			return get_estimated_hand_of_death_value( *this, spell );

		case k_ai_spell_type_healing:
			return get_estimated_healing_value( *this, spell, true );

		case k_ai_spell_type_resurrection:
			return get_estimated_healing_value( *this, spell, false );
	}
	return 0.0;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_spellcasting_value_per_hit() const
{
	if (m_ai_data->spellcasting_value_per_hit < 0.0)
	{
		double result = 0.0;

		// we don't compute spellcasting value for heroes that belong
		// to humans below Hard difficulty.
		if (get_hero() != 0 && m_battlefield.get_difficulty() < k_difficulty_hard
			&& !m_battlefield.is_computer_controlled( get_controller() ))
			result = 0.0;
		else if (can_cast_spells())
		{
			// find a list of our damage spells
			t_spell	spell;
			double	value;

			for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
			{
				if (!can_cast( spell ))
					continue;
				value = get_ai_estimated_value( spell );
				if (result < value)
					result = value;
			}
			if (get_number() > 0)
				result /= get_total_hits();
			else
				result /= get_hit_points();
			if (is_summoned())
				result *= 0.5;
		}
		m_ai_data->spellcasting_value_per_hit = result;
	}
	return m_ai_data->spellcasting_value_per_hit;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_tactics_value_per_hit() const
{
	if (m_ai_data->tactics_value_per_hit < 0.0)
	{
		// below Hard difficulty, we ignore the tactics value for player
		// controlled heroes.
		if (get_hero() != 0 && m_battlefield.get_difficulty() < k_difficulty_hard
			&& !m_battlefield.is_computer_controlled( get_controller() ))
		{
			m_ai_data->tactics_value_per_hit = 0.0;
			return 0.0;
		}
		
		t_influence_list::const_iterator index = m_influenced_creatures.begin();
		t_influence_list::const_iterator end = m_influenced_creatures.end();

		double						offense;
		double						defense;
		double						damage;
		double						result = 0.0;
		t_creature_influence const* influence;
		t_combat_creature const*	creature;

		// value is a measure of how much damage, per turn, this creature contributes through
		// use of artifacts and skill.
		// offense is damage * bonus.
		// defense increase assumes that it will take 5 turns to eliminate the target, so
		// we've increased the damage it does by 1/5th of the actual defense increase, per turn.
		for (; index != end; ++index)
		{
			influence = *index;
			offense = (influence->artifact_offense_bonus + influence->offense_bonus) * 0.01;
			defense = (influence->artifact_defense_bonus + influence->defense_bonus) * 0.01;
			creature = influence->creature;
			damage = (creature->get_damage_low() + creature->get_damage_high()) * 0.5
					* creature->get_number();
			offense = damage * offense;
			defense = damage * defense * 0.2;
			if (creature->get_controller() == get_controller())
				result += offense + defense;
			else
				result -= offense + defense;
		}

		result /= get_hit_points();
		if (get_number() > 0)
			result /= get_number();
		m_ai_data->tactics_value_per_hit = result;
	}
	return m_ai_data->tactics_value_per_hit;
}

// ---------------------------------------------------------------
// compute the value of a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_value_per_hit() const
{
	if (m_ai_data->value_per_hit < 0.0)
	{
		double	result;
		double	ranged_value;
		double  spell_value;
		double	tactics_value;

		result = get_ai_melee_value_per_hit();
		ranged_value = get_ai_ranged_value_per_hit();
		if (result < ranged_value)
			result = ranged_value;
		spell_value = get_ai_spellcasting_value_per_hit();
		if (result < spell_value)
			result = spell_value;
		tactics_value = get_ai_tactics_value_per_hit();
		if (result < tactics_value)
			result = tactics_value;

		m_ai_data->value_per_hit = result;
	}
	return m_ai_data->value_per_hit;
}

// ---------------------------------------------------------------
// get value of damaging a creature
// ---------------------------------------------------------------
double t_combat_creature::get_ai_value( int damage ) const
{
	if (damage < 0)
		return 0.0;

	// base result is 75% of damage
	double  result = damage * 0.75;
	int		dead;
	int		hit_points = get_hit_points();
	
	// reduce damage by spells
	if (is_active( k_spell_celestial_armor ))
		damage -= m_spells[k_spell_celestial_armor].power;
	if (is_active( k_spell_life_drain ))
		damage -= m_spells[k_spell_life_drain].power;
	if (damage < 0)
		damage = 0;
	damage += m_wounds;
	dead = damage / hit_points;
	if (is_active(k_spell_guardian_angel))
		dead -= m_spells[k_spell_guardian_angel].power;
	if (dead > m_number)
		dead = m_number;
	damage -= dead * hit_points;

	// add a 25% bonus for wounds left on the creature after deaths.  This brings
	// the total to 100% for damage done that does not inflict deaths, plus 25% of the
	// value of any wounds the creature already had.
	result += damage * 0.25;
	// add a 50% bonus for each creature killed.  This brings the total for creatures killed
	// to 125%.
	result += dead * hit_points * 0.5;
	result *= get_ai_value_per_hit();

	// mark a 50% penalty for attacking a creature under our control or berserk
	if (is_active( k_spell_hypnotize ) || is_active( k_spell_short_hypnotism )
		|| is_active( k_spell_berserk ))
		result = result * 0.5;
	return result;
}


// ------------------------------------------------------------------------------
// combat actor
// recharge wands
// ------------------------------------------------------------------------------
static void recharge_wands( t_combat_creature& creature )
{
	t_artifact_slot slot;
	t_artifact_effect_list::const_iterator index;
	t_artifact_effect_list::const_iterator end;
	t_artifact_prop::t_spell_charges*      charges;

	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr(slot))
	{
		t_artifact const&			  artifact = creature.get_artifact( slot );
		t_artifact_effect_list const& effects = artifact.get_effects();

		end = effects.end();
		for (index = effects.begin(); index != end; index++)
		{
			charges = dynamic_cast<t_artifact_prop::t_spell_charges*>(index->get());
			if (charges != 0)
				charges->recharge();
		}
	}
}

// ------------------------------------------------------------------------------
// combat actor
// reset flags for a new turn
// ------------------------------------------------------------------------------
int const k_retaliation_turn_increment = 3000000;

void t_combat_creature::process_new_turn()
{
	m_has_moved = false;
	m_plague_checked = false;
	m_poison_checked = false;
	m_has_regenerated = false;
	m_has_leeched = false;
	m_waiting = false;
	m_ai_data->do_not_wait = false;
	if (!is_summoned())
		recharge_wands( *this );
	check_morale();
	if (m_has_retaliated)
		m_retaliation_time_stamp += k_retaliation_turn_increment;
}

// ------------------------------------------------------------------------------
// combat actor
// play a given action's sound
// ------------------------------------------------------------------------------
t_playing_sound_ptr t_combat_creature::play_sound( t_combat_actor_action_id action, 
												   bool looping ) const
{
	if (m_sounds[action] != 0)
		return m_sounds[action]->play( get_sound_volume(), looping );
	return t_playing_sound_ptr();
}

// ------------------------------------------------------------------------------
// combat actor
// Is this creature a ranged creature?
// ------------------------------------------------------------------------------
bool t_combat_creature::is_ranged() const
{
	if (!has_ability( k_ability_ranged ))
		return false;
	if (get_shots() <= 0)
		return false;
	return true;
}

// ------------------------------------------------------------------------------
// begin an animation
// ------------------------------------------------------------------------------
void t_combat_creature::set_animation( t_combat_actor_action_id action,
								       t_direction direction,
									   t_combat_creature_handler handler )
{
	set_animation( action, direction, handler, t_combat_action_message() );
}

// ------------------------------------------------------------------------------
// begin an animation
// ------------------------------------------------------------------------------
void t_combat_creature::set_animation( t_combat_actor_action_id action,
								       t_direction direction,
									   t_combat_creature_handler handler,
									   t_combat_action_message const & message )
{
	t_play_combat_animation_ptr animation;

	animation = new t_play_combat_animation( *this, direction, action );
	animation->display_action_message( message );
	animation->set_end_handler( handler );
	m_animation = animation.get();
}

// ------------------------------------------------------------------------------
// begin an animation
// ------------------------------------------------------------------------------
void t_combat_creature::set_animation( t_action_id action, t_direction direction )
{
	set_animation( action, direction, t_combat_action_message() );
}

// ------------------------------------------------------------------------------
// begin an animation
// ------------------------------------------------------------------------------
void t_combat_creature::set_animation( t_action_id action, 
									   t_direction direction,
									   t_combat_action_message const & message )
{
	t_combat_creature_handler handler;

	if (action == k_combat_actor_action_wait)
	{
		assert( !message.is_displayable() ); // Wait animations never display a message
		set_wait_animation();
		return;
	}
	if (action != k_combat_actor_action_fidget)
		handler = bound_handler( m_battlefield, &t_battlefield::end_animation );
	set_animation( action, direction, handler, message );
}


// ------------------------------------------------------------------------------
// add to list of actors
// ------------------------------------------------------------------------------
void t_combat_creature::on_placed()
{
	m_battlefield.add( this );
	if (get_number() > 0)
	{
		m_battlefield.add_threat( this );
		if (m_label != 0 && m_label->get_list_position()._Mynode() == NULL )
		{
			m_battlefield.place_object( m_label, m_label->compute_position() );
		}
	}
}

// ------------------------------------------------------------------------------
// remove from list of actors
// ------------------------------------------------------------------------------
void t_combat_creature::on_removed()
{
	m_battlefield.remove_threat( this );
	m_battlefield.remove( this );
}

// ------------------------------------------------------------------------------
// return adjusted speed
// ------------------------------------------------------------------------------
int t_combat_creature::get_speed() const
{
	int result = m_creature_stack->get_speed() + m_tactics_speed_bonus;

	if (m_battlefield.is_sea_battle() && has_ability( k_ability_sea_bonus ))
		result += 2;
	if (is_active( k_spell_speed ))
		result += 3;
	if (is_active( k_spell_haste ) || is_active( k_spell_nature_haste ))
		result += k_haste_speed_increase;
	if (is_active( k_spell_aging ))
		result >>= 1;
	if (is_active( k_spell_slow ) || is_active( k_spell_order_slow )
		|| is_active( k_spell_bind_flyer ))
		result >>= 1;
	return result;
}

t_combat_label* t_combat_creature::get_label() const
{
	return m_label;
}
// ------------------------------------------------------------------------------
// set pointer to label with # of creatures
// ------------------------------------------------------------------------------
void t_combat_creature::set_label( t_combat_label* label )
{
	m_label = label;
}

// ------------------------------------------------------------------------------
// before turning, remove threat mask
// ------------------------------------------------------------------------------
void t_combat_creature::on_turning()
{
	m_battlefield.remove_threat( this );
}

// ------------------------------------------------------------------------------
// after turning, add threat
// ------------------------------------------------------------------------------
void t_combat_creature::on_turned( )
{
	m_battlefield.add_threat( this );
}

// ------------------------------------------------------------------------------
// after moving, move label
// ------------------------------------------------------------------------------
void t_combat_creature::on_moving()
{
	m_battlefield.remove_threat( this );
}

void t_combat_creature::refresh_label_position( bool check_overlap )
{
	if (m_label == 0)
		return;
	if (get_number() == 0)
		return;

	m_battlefield.move_object( *m_label, m_label->compute_position( check_overlap ) );
}

// ------------------------------------------------------------------------------
// after moving, move label
// ------------------------------------------------------------------------------
void t_combat_creature::on_moved( bool cell_changed )
{
	t_battlefield& battlefield = m_battlefield;

	if (cell_changed)
		battlefield.add_threat( this );
	battlefield.set_creature_drawing_order( *this );
	refresh_label_position( true );
	if (m_active_spell_animation_window != 0)
	{
		t_screen_point point = get_animation_position( m_active_spell_animation );

		m_active_spell_animation_window->move( point );
	}
}

// ------------------------------------------------------------------------------
// return creature's abilities.  Expand later for spell effects.
// ------------------------------------------------------------------------------
bool t_combat_creature::has_ability( t_creature_ability ability ) const
{
	switch (ability)
	{
		case k_ability_area_effect:
			if (m_cheating_state == k_cheating_jack)
				return true;
			break;

		case k_ability_block:
			if (m_block_chance > 0)
				return true;
			break;
		case k_ability_chaos_protection:
			if (is_active( k_spell_protection_from_chaos ))
				return true;
			break;
		case k_ability_cold_resistance:
			if (is_active( k_spell_cold_resistance ))
				return true;
			break;
		case k_ability_death_protection:
			if (is_active( k_spell_protection_from_death ))
				return true;
			break;
		case k_ability_fire_resistance:
			if (is_active( k_spell_fire_resistance ))
				return true;
			break;
		case k_ability_first_strike:
			if (is_active( k_spell_first_strike ) || is_active( k_spell_snake_strike ))
				return true;
			break;

		case k_ability_freeze:
			if (is_active( k_spell_freezing_attack ))
				return true;
			break;

		case k_ability_life_protection:
			if (is_active( k_spell_protection_from_life ))
				return true;
			break;

		case k_ability_long_range:
		case k_ability_siege_machine:
			if (is_in_tower())
				return true;
			if (is_active( k_spell_precision ))
				return true;
			break;

		case k_ability_magic_immunity:
			if (is_active( k_spell_anti_magic ))
				return true;
			break;
		case k_ability_magic_mirror:
			if (is_active( k_spell_magic_mirror ))
				return true;
			break;

		case k_ability_mechanical:
			if (is_active( k_spell_illusionary_ally ) || is_active( k_spell_clone ))
				return true;
			break;

		case k_ability_ranged:
			if (m_cheating_state != k_not_cheating)
				return true;
			break;

		case k_ability_undead:
			if (is_active( k_spell_animate_dead ) || is_active( k_spell_death_call ))
				return true;
			break;
	};
	return m_has_ability[ability];
}

// ------------------------------------------------------------------------------
// inflict damage on a target
// ------------------------------------------------------------------------------
static int get_damage_bonus( t_combat_creature const& creature, int result, bool round_up )
{
	if (creature.is_active(k_spell_dragon_strength ))
		result += result;
	if (round_up)
	{
		if (creature.is_active( k_spell_strength ))
			result += (result + 3) / 4;
		if (creature.is_active( k_spell_giant_strength ))
			result += (result + 3) / 4;
	}
	else
	{
		if (creature.is_active( k_spell_strength ))
			result += result / 4;
		if (creature.is_active( k_spell_giant_strength ))
			result += result / 4;
	}
	return result;
}

int t_combat_creature::get_damage_low() const
{
	return get_damage_low( k_spell_none, k_spell_none);
}

int t_combat_creature::get_damage_low( t_spell add_effect, t_spell ignore_effect ) const
{
	int result;

	if (consider_active( k_spell_bless, add_effect, ignore_effect ))
	{
		result = m_creature_stack->get_damage_high();
		return get_damage_bonus( *this, result, true );
	}
	result = m_creature_stack->get_damage_low();
	return get_damage_bonus( *this, result, false );
}

// ------------------------------------------------------------------------------
// inflict damage on a target
// ------------------------------------------------------------------------------
int t_combat_creature::get_damage_high() const
{
	return get_damage_high( k_spell_none, k_spell_none);
}


int t_combat_creature::get_damage_high( t_spell add_effect, t_spell ignore_effect) const
{
	int result;

	if (consider_active( k_spell_curse, add_effect, ignore_effect ))
	{
		result = m_creature_stack->get_damage_low();
		return get_damage_bonus( *this, result, false );
	}
	result = m_creature_stack->get_damage_high();
	return get_damage_bonus( *this, result, true );
}

// ------------------------------------------------------------------------------
// get overall general bonus to damage;
// ------------------------------------------------------------------------------
float t_combat_creature::get_offense( bool ranged) const
{
	float result = 1.0f + m_offense_bonus / 100.0f;

	if (!ranged && has_ability( k_ability_normal_melee ) 
		&& !m_creature_stack->has_ability( k_ability_normal_melee ))
		result *= m_creature_stack->get_offense( true );
	else
		result *= m_creature_stack->get_offense( ranged );

	if (is_in_tower())
		result *= 2.0f;
	if (!ranged)
	{
		if (is_active( k_spell_bloodlust ))
			result *= 1.25f;
		if (m_in_water)
			result *= 0.75f;
	}
	else
	{
		if (is_active( k_spell_precision ))
			result *= 1.25f;
	}
	if (has_ability( k_ability_sea_bonus) && m_battlefield.is_sea_battle())
		result *= 1.5f;
	if (is_active( k_spell_prayer ))
		result *= 1.25f;
	if (is_active( k_spell_binding ))
		result *= 0.5f;
	if (is_active( k_spell_aging ))
		result *= 0.75f;
	if (is_active( k_spell_weakness ))
		result *= 0.75f;
	if (is_active( k_spell_evil_hour ))
		result *= 0.8f;
	if (is_active( k_spell_gas ))
		result *= 0.85f;
	if (m_got_good_morale)
		result *= 1.25f;
	if (m_got_bad_morale)
		result *= 0.8f; // same as result / 1.25f
	if (m_cheating_state != k_not_cheating)
		result += 10.0;
	return result;
}

// ------------------------------------------------------------------------------
// adjust amount of damage this creature does to the target for special
// abilities, spells, etc
// ------------------------------------------------------------------------------
int  t_combat_creature::adjust_attack_damage(int base_damage, t_attackable_object const& target, 
										     bool ranged, int distance_moved,
											 t_wall_bonus wall_bonus ) const
{
	int damage = base_damage;
	t_battlefield &battlefield = m_battlefield;

	if (!ranged && has_ability( k_ability_charging ))
	{
		t_battlefield::adjust_for_charging_bonus(damage, distance_moved);
	}

	if (target.is_giant())
	{
		bool has_giantslayer;
		
		has_giantslayer = has_ability( k_ability_giantslayer );
		if (ranged)
			has_giantslayer = has_giantslayer || has_ability( k_ability_ranged_giantslayer );
		else
			has_giantslayer = has_giantslayer || has_ability( k_ability_melee_giantslayer );
		if (has_giantslayer)
		{
			damage *= 2;
		}
		else if (is_active(k_spell_slayer))
		{
			damage += damage >> 1;
		}
	}

	switch (wall_bonus)
	{
		case k_wall_bonus_attack:
			damage = damage * battlefield.get_castle_wall_bonus();
			break;

		case k_wall_bonus_defense:
			damage = damage / battlefield.get_castle_wall_bonus();
			break;
	}

	float offense;

	offense = get_offense( ranged );
	damage = damage * get_damage_modifier( target, ranged, offense );
	switch (get_alignment())
	{
		case k_town_life:
			if (target.has_ability( k_ability_life_protection ) 
				|| target.is_active( k_spell_protection_from_life ))
				damage = damage * 100 / 150;
			break;
		case k_town_order:
			if (target.is_active( k_spell_protection_from_order ))
				damage = damage * 100 / 150;		
			break;
		case k_town_death:
			if (target.is_active( k_spell_protection_from_death )
				|| target.has_ability( k_ability_death_protection ))
				damage = damage * 100 / 150;
			break;
		case k_town_chaos:
			if (target.is_active( k_spell_protection_from_chaos )
				|| target.has_ability( k_ability_chaos_protection ))
				damage = damage * 100 / 150;
			break;
		case k_town_nature:
			if (target.is_active( k_spell_protection_from_nature ))
				damage = damage * 100 / 150;
			break;
	}
	// note: moved adjustment for melee penalty to ranged creatures into t_creature
	if ((has_ability( k_ability_fire_attack ) || has_ability( k_ability_breath_attack))
		&& target.has_ability( k_ability_fire_resistance ))
		damage >>= 1;
	if (has_ability( k_ability_cold_attack ) && target.has_ability( k_ability_cold_resistance ))
		damage >>= 1;
	if (damage < 1)
		damage = 1;

	return damage;
}

// ------------------------------------------------------------------------------
// calculate amount of damage to inflict on a target
// ------------------------------------------------------------------------------
int t_combat_creature::get_damage( t_attackable_object const& target, bool ranged,
								   int distance_moved, t_wall_bonus wall_bonus ) const
{
	int damage_low  = get_damage_low();
	int damage_high = get_damage_high();
	int i;
	int damage      = 0;

	if (m_number < 10)
	{
		for (i = 0; i < m_number; i++)
			damage += random( damage_low, damage_high );
	}
	else
	{
		for (i = 0; i < 10; i++)
			damage += random( damage_low, damage_high );
		damage = (damage * m_number) / 10;
	}

	return adjust_attack_damage(damage, target, ranged, distance_moved, wall_bonus );
}

// ------------------------------------------------------------------------------
// Get the maximum total damage one attack will do to the target
// ------------------------------------------------------------------------------
int	t_combat_creature::get_total_damage_high( t_attackable_object const& target, bool ranged,
											  int distance_moved, t_wall_bonus wall_bonus ) const
{
	int damage_high = get_damage_high() * m_number;

	return adjust_attack_damage( damage_high, target, ranged, distance_moved, wall_bonus );
}

// ------------------------------------------------------------------------------
// Get the minimum total damage one attack will do to the target
// ------------------------------------------------------------------------------
int	t_combat_creature::get_total_damage_low( t_attackable_object const& target, bool ranged,
											 int distance_moved, t_wall_bonus wall_bonus ) const
{
	int damage_low = get_damage_low() * m_number;

	return adjust_attack_damage( damage_low, target, ranged, distance_moved, wall_bonus );
}


// ------------------------------------------------------------------------------
// get number of creatures remaining after receiving X damage
// ------------------------------------------------------------------------------
int t_combat_creature::get_adjusted_number( int damage ) const
{
	int number = m_number;

	if (damage <= 0)
		return number;

	// Adjust number for damage taken
	if (is_active( k_spell_celestial_armor ))
		damage -= m_spells[k_spell_celestial_armor].power;
	if (is_active( k_spell_life_drain ))
		damage -= m_spells[k_spell_life_drain].power;

	if (damage <= 0)
		return number;

	int hit_points = get_hit_points();
	int dead;

	dead = ((damage + m_wounds) / hit_points);
	if (is_active(k_spell_guardian_angel))
		dead -= m_spells[k_spell_guardian_angel].power;

	if (dead > 0)
		number -= dead;

	if (number <= 0)
		return 0;
	return number;
}

// ------------------------------------------------------------------------------
// adjust projected damage
// ------------------------------------------------------------------------------
static int adjust_projected_damage( t_combat_creature const&	attacker,
								    int							attacker_number,
								    t_attackable_object const&	target,
									int							old_damage,
									int							new_damage )
{
	if (attacker.has_ability(k_ability_stone_gaze) && target.is_living_creature())
	{
		// Count as damage
		// Only creatures get stoned, not rocks, gates, etc
		const t_combat_creature* target_creature = dynamic_cast<const t_combat_creature*>( &target );
		if (target_creature)
		{
			int stoning_chance = attacker.get_stoning_chance( *target_creature );

			if (stoning_chance * attacker_number >= 500)
			{
				double avg_killed = (double)stoning_chance * (double)attacker_number / 1000.0;
				new_damage += (int)(avg_killed * target_creature->get_hit_points());
			}
		}
	}

	// Can't do more damage than the target has hitpoints
	int target_total_hits = target.get_total_hits(true);

	target_total_hits -= old_damage;
	if (target_total_hits < 0)
		target_total_hits = 0;

	if ( new_damage > target_total_hits)
	{
		new_damage = target_total_hits;

		// TODO: Account for Guardian Angel, other damage reducers
	}

	// Account for blocking (30% chance)
	if (target.has_ability(k_ability_block))
	{
		new_damage = ((new_damage * 7) + 5 ) / 10; 
	}
	return new_damage;
}


// ------------------------------------------------------------------------------
// Project the average amount of damage this creature stack would inflict on 
// the given target given certain changes to the battlefield, such as the 
// attacker or target having already taken damage
// ------------------------------------------------------------------------------
int t_combat_creature::get_melee_damage( t_attackable_object const&	target,
										 int						distance_moved ) const
{
	int damage_low  = get_damage_low();
	int damage_high = get_damage_high();
	int damage		= (damage_low + damage_high) * m_number / 2;

	if (distance_moved < 0)
	{
		distance_moved = get_combat_movement();
	}

	damage = adjust_attack_damage(damage, target, false, distance_moved, k_wall_bonus_none );
	damage = adjust_projected_damage( *this, m_number, target, 0, damage );
	return damage;
}

// ------------------------------------------------------------------------------
// Project the average amount of damage this creature stack would inflict on 
// the given target given certain changes to the battlefield, such as the 
// attacker or target having already taken damage
// ------------------------------------------------------------------------------
void t_combat_creature::get_melee_effect( t_combat_creature const&	target,
										  int						distance_moved,
										  t_creature_int_map&		damage_map ) const
{
	int number;
	int attacker_damage = damage_map[this];

	number = get_adjusted_number( attacker_damage );
	if (number <= 0)
		return;

	int& target_damage	= damage_map[&target];
	int  damage_low		= get_damage_low();
	int  damage_high	= get_damage_high();
	int  damage			= (damage_low + damage_high) * number / 2;
	int  movement		= get_combat_movement();

	if (distance_moved < 0 || distance_moved > movement)
	{
		distance_moved = movement;
	}

	damage = adjust_attack_damage(damage, target, false, distance_moved, k_wall_bonus_none );
	damage = adjust_projected_damage( *this, number, target, target_damage, damage );
	target_damage += damage;
}

// ------------------------------------------------------------------------------
// Project the average amount of damage this creature stack would inflict on 
// the given target given certain changes to the battlefield, such as the 
// attacker or target having already taken damage.  Include any and all area effects,
// such as hydras or dragon breath
// ------------------------------------------------------------------------------
void t_combat_creature::get_melee_effect( t_combat_creature const&	target,
										  int						distance_moved,
										  t_creature_int_map&		damage_map,
										  t_attack_angle const&		attack_angle,
										  t_map_point_2d const&		target_position ) const
{
	t_battlefield& battlefield = m_battlefield;

	t_combat_creature_list creatures;

	get_melee_effect( target, distance_moved, damage_map );
	if (battlefield.get_difficulty() >= k_difficulty_normal)
	{
		if (has_ability( k_ability_breath_attack ))
		{
			creatures = battlefield.get_breath_targets( *this, target, attack_angle.point, 
														target_position, true );
		}
		else if (has_ability( k_ability_hydra_strike ) || has_ability( k_ability_3_headed_attack ))
		{

			t_attackable_object_list			targets;
			t_attackable_object_list::iterator	target_index;
			t_combat_creature_list				creatures;
			t_combat_creature*					creature;
			t_attack_angle						closest;

			targets = battlefield.get_multi_attack_targets( *this, target, attack_angle );
			for (target_index = targets.begin(); target_index != targets.end(); ++target_index)
			{
				creature = dynamic_cast<t_combat_creature*>( *target_index );
				if (creature != 0 && creature != &target)
					creatures.push_back( creature );
			}
		}
	}

	t_combat_creature_list::iterator index;
	t_combat_creature_list::iterator end = creatures.end();

	for (index = creatures.begin(); index != creatures.end(); ++index)
		get_melee_effect( **index, distance_moved, damage_map );
}


// ------------------------------------------------------------------------------
// Project the average amount of damage this creature stack would inflict on 
// the given target given certain changes to the battlefield, such as the 
// attacker or target having already taken damage
// ------------------------------------------------------------------------------
int t_combat_creature::get_ranged_damage( t_combat_creature const&	target,
										  int						attacker_damage,
										  int						target_damage ) const
{
	if (!is_ranged())
		return 0;

	int number = get_adjusted_number( attacker_damage );

	if (number <= 0)
		return 0;

	assert(attacker_damage >= 0);
	assert(target_damage >= 0);

	int damage_low  = get_damage_low();
	int damage_high = get_damage_high();
	int damage = ((damage_low + damage_high) * number) / 2;
	t_battlefield& battlefield = m_battlefield;

	damage = adjust_attack_damage( damage, target, true, 0, k_wall_bonus_none );

	int range_factor = battlefield.get_range_factor( *this, target );

	if (range_factor < 1)
		range_factor = 1;
	damage /= range_factor;

	damage = adjust_projected_damage( *this, number, target, target_damage, damage );
	return damage;
}

// ------------------------------------------------------------------------------
// Damage if this creature casts this spell
// ------------------------------------------------------------------------------
int t_combat_creature::get_spell_damage( t_combat_creature const& target, t_spell spell ) const
{
	return get_spell_damage( target, spell, m_battlefield.get_grail_data( belongs_to_defender() ) );
}


// ------------------------------------------------------------------------------
// increase number of non-revivable creatures
// ------------------------------------------------------------------------------
void t_combat_creature::increase_permanent_death_count( int deaths )
{
	m_permanent_deaths += deaths;
	if (m_spells[k_spell_guardian_angel].active)
	{
		if (m_spells[k_spell_guardian_angel].power > deaths)
			m_permanent_deaths -= deaths;
		else
			m_permanent_deaths -= m_spells[k_spell_guardian_angel].power;
	}
}

// ------------------------------------------------------------------------------
// recover spell points from mana spell
// ------------------------------------------------------------------------------
void t_combat_creature::increase_spell_points( int amount )
{
	m_spell_points += amount;
}

// ------------------------------------------------------------------------------
// true if creature cannot take any actions
// ------------------------------------------------------------------------------
bool t_combat_creature::is_incapacitated() const
{
	return is_active( k_spell_blind ) || is_active( k_spell_frozen )
		|| is_active( k_spell_stun )  || is_active( k_spell_terror )
		|| is_active( k_spell_confusion )
		|| is_active( k_spell_summon_stinkbug );
}

// ------------------------------------------------------------------------------
// apply pending damage
// ------------------------------------------------------------------------------
void t_combat_creature::resolve_damage()
{
	int old_number = m_number;
	std::string text = format_string( "-%i", m_new_damage );
	int			poison = get_new_poison();
	t_battlefield& battlefield = m_battlefield;
	int				deaths;
	t_combat_action_message damage_message = m_new_damage_message;

	if (poison > 0 )
	{
		if (!is_active( k_spell_poison ))
			set_spell( k_spell_poison, damage_message );
		m_spells[k_spell_poison].power += poison;
		m_new_poison = 0;
	}

	if (m_spells[k_spell_celestial_armor].active)
	{
		int damage_absorbed = m_spells[k_spell_celestial_armor].power;
		
		if (damage_absorbed > m_new_damage)
			damage_absorbed = m_new_damage;

		m_new_damage -= damage_absorbed;
		if ((m_spells[k_spell_celestial_armor].power -= damage_absorbed) == 0)
			clear_spell( k_spell_celestial_armor, damage_message );
		battlefield.add_text( *this, format_string( "-%i", damage_absorbed ),
		                      get_message_color(), k_message_icon_shield );
	}

	int displayed_damage = m_new_damage;

	if (m_spells[k_spell_life_drain].active)
	{
		if (m_spells[k_spell_life_drain].power > m_new_damage)
		{
			m_spells[k_spell_life_drain].power -= m_new_damage;
			m_new_damage = 0;
		}
		else
		{
			m_new_damage -= m_spells[k_spell_life_drain].power;
			clear_spell( k_spell_life_drain, damage_message );
		}
	}

	m_wounds += m_new_damage;
	deaths = m_wounds / get_hit_points();
	if (displayed_damage > 0 || deaths > 0)
		battlefield.add_damage_text( *this, displayed_damage, deaths );
	m_new_damage = 0;
	m_new_damage_message.clear();
	m_got_good_luck = false;
	m_got_bad_luck = false;
	if (is_active( k_spell_blind ))
		clear_spell( k_spell_blind, damage_message );
	if (deaths == 0)
	{
		m_new_damage_is_permanent = false;
		return;
	}
	
	if (deaths > m_number)
		deaths = m_number;
	m_wounds -= deaths * get_hit_points();
	m_number -= deaths;
	m_guardian_angel_limit = deaths;
	if (m_new_damage_is_permanent)
		increase_permanent_death_count( deaths );
	m_new_damage_is_permanent = false;
	if (m_number <= 0)
	{
		m_number = 0;
		m_wounds = 0;
		clear_influence();
	}
	else if (deaths > 0 && is_active( k_spell_guardian_angel ))
		use_guardian_angel();
	m_battlefield.invalidate( *m_label );
}

// -----------------------------------------------------------------------
// return how much poison increases this round
// -----------------------------------------------------------------------
int t_combat_creature::get_new_poison() const
{
	int result = m_new_poison;

	if (is_active( k_spell_poison ))
		result -= get_effect_power( k_spell_poison );
	if (result < 0)
		result = 0;
	return result;
}


// -----------------------------------------------------------------------
// apply any damage the creature may have received
// -----------------------------------------------------------------------
void t_combat_creature::apply_damage( t_battlefield& battlefield, bool show_death )
{
	int damage = get_new_damage();
	int poison = get_new_poison();

	if (damage == 0 && poison == 0)
		return;

	t_combat_action_message damage_message = m_new_damage_message; // resolve_damage clears m_new_damage_message
	// run good or bad luck animation
	resolve_damage();
	// if the actor died, start the death animation if it hasn't started already.
	if (get_number() == 0)
	{
		animate_death( show_death, damage_message );
		return;
	}

	// show poison animation
	if (poison > 0)
	{
		t_combat_action_message poison_message( *m_new_poison_source, 
												replace_keywords( k_combat_action_cause_effect, "%spell", get_short_spell_name( k_spell_poison ) ) );
		m_battlefield.start_animation( *this, k_spell_poison, poison_message );
	}
}


// ------------------------------------------------------------------------------
// check if actor is dead - if it is, it doesn't block movement.
// ------------------------------------------------------------------------------
bool t_combat_creature::blocks_movement() const
{
	return m_number > 0;
}

std::string t_combat_creature::get_object_name() const
{
	return m_creature_stack->get_name( false, m_number );
}


// ------------------------------------------------------------------------------
// get skill levels
// ------------------------------------------------------------------------------

int	t_combat_creature::get_tactics_defense_bonus() const
{
	return m_creature_stack->get_tactics_defense_bonus( m_battlefield.is_sea_battle() );
}

int t_combat_creature::get_tactics_move_bonus() const
{
	return m_creature_stack->get_tactics_move_bonus( m_battlefield.is_sea_battle() );
}

int	t_combat_creature::get_tactics_offense_bonus() const
{
	return m_creature_stack->get_tactics_offense_bonus( m_battlefield.is_sea_battle() );
}

int	t_combat_creature::get_tactics_speed_bonus() const
{
	return m_creature_stack->get_tactics_speed_bonus( m_battlefield.is_sea_battle() );
}
	


// ------------------------------------------------------------------------------
// get distance for melee attack
// ------------------------------------------------------------------------------
int t_combat_creature::get_attack_range() const
{
	if (has_ability( k_ability_long_weapon ))
		return 6;
	return 2;
}

t_missile_type t_combat_creature::get_missile_type() const
{
	if (m_cheating_state == k_cheating_jack)
		return k_missile_boulder;
	return m_creature_stack->get_missile_type();
}

// ------------------------------------------------------------------------------
// get value (to AI) of interfering with this creature's ranged attack
// ------------------------------------------------------------------------------
double t_combat_creature::get_interference_bonus(void) const
{
	return project_interference_bonus(0);
}

// ------------------------------------------------------------------------------
// get value (to AI) of interfering with this creature's ranged attack
// after it has taken damage
// ------------------------------------------------------------------------------
double t_combat_creature::project_interference_bonus(int damage) const
{
	if (!is_ranged())
		return 0.0;

	int total_hits = get_total_hits();

	if (damage >= total_hits)
		return 0.0;

	int		number = get_adjusted_number( damage );
	double	average_damage;
	double	ranged_damage;
	double	melee_damage;

	average_damage = (get_damage_low() + get_damage_high()) * 0.5;
	ranged_damage = average_damage * get_offense( true ) * (get_attacks( true ) + 1) * 0.5;
	melee_damage = average_damage * get_offense( false ) * (get_attacks( false ) + 1) * 0.5;
	if (ranged_damage < melee_damage)
		return 0.0;
	return (ranged_damage - melee_damage) * number;
}


// ------------------------------------------------------------------------------
// get movement in combat.
// ------------------------------------------------------------------------------
int t_combat_creature::get_combat_movement() const
{
	int result = m_creature_stack->get_combat_movement() + m_move_bonus;

	result += m_artifact_move_bonus;
	if (m_battlefield.is_sea_battle() && has_ability( k_ability_sea_bonus ))
		result += 600;
	if (is_active( k_spell_haste ) || is_active( k_spell_nature_haste ))
		result += k_haste_move_increase;
	if (is_active( k_spell_aging ))
		result >>= 1;
	if (is_active( k_spell_slow ) || is_active( k_spell_order_slow )
		|| is_active( k_spell_bind_flyer))
		result >>= 1;
	switch (m_cheating_state)
	{
		case k_cheating_adam:
			result += 15000;
			break;

		case k_cheating_jack:
			if (result > 1200)
				result = 1200;
			break;
	}
	if (result < 0)
		result = 0;
	return result;
}

double t_combat_creature::get_combat_value() const
{
	int total_hits = get_total_hits();

	if (total_hits == 0)
		return 0.0;

	// Basic combat value
	double			melee_attack = (get_damage_low() + get_damage_high()) * (get_attacks( false ) + 1)
								  * 0.25 * get_offense( false ) * get_number();
	double			ranged_attack = (get_damage_low() + get_damage_high()) * (get_attacks( true ) + 1)
								  * 0.25 * get_offense( true ) * get_number();
	double			melee_defense = total_hits * get_defense_basic( false ) * get_defense_bonus( false );
	double			ranged_defense = total_hits * get_defense_basic( true ) * get_defense_bonus( true );
	double			defense = (melee_defense + ranged_defense) * 0.5;
	double			spell_attack = get_ai_spellcasting_value_per_hit() * total_hits;
	double			attack = melee_attack;
	t_battlefield&	battlefield = m_battlefield;

	if (attack < ranged_attack && battlefield.can_shoot( *this ))
		attack = ranged_attack;
	if (attack < spell_attack)
		attack = spell_attack;

	return sqrt( attack * defense );
}


int t_combat_creature::get_hit_points(t_spell ignore_effect) const
{
	int result = m_creature_stack->get_hit_points();

	if (consider_active( k_spell_giant_strength, k_spell_none, ignore_effect ))
		result += (result + 2) >> 2;
	if (consider_active( k_spell_health_boost, k_spell_none, ignore_effect ))
		result += (result + 2) >> 2;
	if (consider_active( k_spell_prayer, k_spell_none, ignore_effect ))
		result += (result + 2) >> 2;
	if (consider_active( k_spell_dragon_strength, k_spell_none, ignore_effect ))
		result += result;
	return result;
}

int t_combat_creature::get_hit_points() const
{
	return get_hit_points(k_spell_none);
}

// ------------------------------------------------------------------------------
// get non-adjusted luck
// ------------------------------------------------------------------------------
int t_combat_creature::get_base_luck() const
{
	return m_creature_stack->get_luck();
}

// ------------------------------------------------------------------------------
// get adjusted luck
// ------------------------------------------------------------------------------
int	t_combat_creature::get_luck_without_spells() const
{
	return get_base_luck() + m_luck_bonus;
}

int t_combat_creature::get_luck() const
{
	if (is_active(k_spell_fortune))
		return 10;
	if (is_active( k_spell_misfortune ))
		return -10;

	int result = get_luck_without_spells();

	if (is_active( k_spell_luck_boost ))
		result += 5;

	if (result < -10)
		result = -10;
	if (result > 10)
		result = 10;

	return result;
}


// ------------------------------------------------------------------------------
// get adjusted morale
// ------------------------------------------------------------------------------
int t_combat_creature::get_morale_without_spells() const
{
	if (has_ability( k_ability_mechanical ) || has_ability( k_ability_undead ))
		return 0;

	int result = m_morale + m_morale_bonus;

	return result;
}

// ------------------------------------------------------------------------------
// get adjusted morale
// ------------------------------------------------------------------------------
int t_combat_creature::get_morale() const
{
	// These spells are checked before the creature type test
	// because they can only be placed here by the Crown of the Mind,
	// whose ability is to place these spells on mechanical / undead creatures.
	if (is_active( k_spell_sorrow ))
		return -10;
	if (is_active( k_spell_spiritual_fervor ))
		return 10;
	if (has_ability( k_ability_mechanical ) || has_ability( k_ability_undead ))
		return 0;

	int result = m_morale + m_morale_bonus;

	if (is_active( k_spell_morale_boost ))
		result += 5;
	if (is_active( k_spell_despair ))
		result -= 10;
	if (result < -10)
		result = -10;
	if (result > 10)
		result = 10;
	return result;
}

int t_combat_creature::get_arc_attack() const
{
	int arc = 90;
	if (has_ability( k_ability_3_headed_attack ))
		arc = 135;
	if (has_ability( k_ability_hydra_strike ))
		arc = 360;
	return arc;
}

int t_combat_creature::get_arc_threat() const
{
	int arc = 200;
	if (has_ability( k_ability_3_headed_attack ))
		arc = 200;
	if (has_ability( k_ability_hydra_strike ))
		arc = 360;
	return arc;
}

// ------------------------------------------------------------------------------
// get area which impedes other creature's movement
// ------------------------------------------------------------------------------
t_threat_footprint const& t_combat_creature::get_threat_footprint() const
{
	int size = get_attack_range() * 2 + get_footprint_size();
	int arc = get_arc_threat();
	return ::get_threat_footprint( size, m_current_direction, arc );
}



// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_combat_creature::clear_all_spells( t_combat_action_message const & message )
{
	t_spell_iterator index;
	t_spell_iterator next;

	next = m_active_spells.begin();
	while (next != m_active_spells.end())
	{
		index = next;
		next++;
		assert( is_active( *index ));
		if (!is_physical( *index ) && can_remove( *index ))
			clear_spell( *index, message );
	}
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_combat_creature::clear_spells( t_town_type alignment, t_combat_action_message const & message )
{
	t_spell_iterator index;
	t_spell_iterator next;

	next = m_active_spells.begin();
	while (next != m_active_spells.end())
	{
		index = next;
		next++;
		assert( is_active( *index ));
		if (!is_physical( *index ) && get_spell_alignment( *index ) == alignment 
			&& can_remove( *index ))
			clear_spell( *index, message );
	}
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
static void cast_cancellation( t_combat_creature* target, t_combat_action_message const & message )
{
	t_spell_list const& list = target->get_active_spells();
	t_spell_list::const_iterator index;
	t_spell_list::const_iterator next;

	next = list.begin();
	while (next != list.end())
	{
		index = next;
		next++;
		assert( target->is_active( *index ));
		if (!is_physical( *index ) 
			&& spell_is( *index, k_spell_bit_blessing | k_spell_bit_summoning )
			&& target->can_remove( *index ) )
			target->clear_spell( *index, message );
	}
}

// ------------------------------------------------------------------------------
// cast a given spell on a creature
// ------------------------------------------------------------------------------
void t_combat_creature::set_spell( t_spell spell, t_combat_action_message const & message, int power, t_spell_source source )
{
	t_spell_effect& effect = m_spells[spell];
	int				duration = 0;

	switch (spell)
	{
		case k_spell_anti_magic:
			clear_all_spells( message );
			break;

		case k_spell_berserk:
			clear_spell( k_spell_song_of_peace, message );
			break;

		case k_spell_bless:
			clear_spell( k_spell_curse, message );
			break;

		case k_spell_blind:
			duration = t_combat_spell::k_duration_blind;
			break;

		case k_spell_cancellation:
			cast_cancellation( this, message );
			return;

		case k_spell_confusion:
			duration = t_combat_spell::k_duration_confusion;
			break;

		case k_spell_guardian_angel:
			if (power > m_number)
				power = m_number;
			break;

		case k_spell_quickness:
			duration = t_combat_spell::k_duration_quickness;
			break;

		case k_spell_stun:
			duration = t_combat_spell::k_duration_stun;
			break;

		case k_spell_summon_stinkbug:
			duration = t_combat_spell::k_duration_summon_stinkbug;
			break;

		case k_spell_terror:
			duration = t_combat_spell::k_duration_terror;
			break;

		case k_spell_cowardice:
			duration = t_combat_spell::k_duration_cowardice;
			break;

		case k_spell_curse:
			clear_spell( k_spell_bless, message );
			break;

		case k_spell_despair:
		case k_spell_sorrow:
			clear_spell( k_spell_spiritual_fervor, message );
			break;

		case k_spell_dispel:
			clear_all_spells( message );
			return;

		case k_spell_fortune:
			clear_spell( k_spell_misfortune, message );
			break;

		case k_spell_frozen:
			duration = t_combat_spell::k_duration_frozen;
			break;

		case k_spell_haste:
		case k_spell_nature_haste:
			clear_spell( k_spell_slow, message );
			clear_spell( k_spell_order_slow, message );
			break;

		case k_spell_hypnotize:
			if (is_active( k_spell_hypnotize ) || is_active( k_spell_short_hypnotism))
			{
				clear_spell( k_spell_hypnotize, message );
				clear_spell( k_spell_short_hypnotism, message );
				return;
			}
			duration = t_combat_spell::k_duration_hypnotize;
			break;

		case k_spell_life_drain:
			if (m_spells[k_spell_life_drain].active)
				power += m_spells[k_spell_life_drain].power;
			break;

		case k_spell_mana:
			increase_spell_points( power );
			return;

		case k_spell_misfortune:
			clear_spell( k_spell_fortune, message );
			break;

		case k_spell_morale_boost:
		case k_spell_spiritual_fervor:
			clear_spell( k_spell_sorrow, message );
			break;

		case k_spell_sacred_ground:
			clear_spell( k_spell_animate_dead, message );
			clear_spell( k_spell_death_call, message );
			clear_spell( k_spell_raise_skeletons, message );
			clear_spell( k_spell_raise_ghost, message );
			clear_spell( k_spell_raise_vampires, message );
			break;

		case k_spell_short_hypnotism:
			if (is_active( k_spell_hypnotize ) || is_active( k_spell_short_hypnotism))
			{
				clear_spell( k_spell_hypnotize, message );
				clear_spell( k_spell_short_hypnotism, message );
				return;
			}
			duration = t_combat_spell::k_duration_short_hypnotism;
			break;

		case k_spell_slow:
		case k_spell_order_slow:
			clear_spell( k_spell_haste, message );
			clear_spell( k_spell_nature_haste, message );
			break;

		case k_spell_song_of_peace:
			clear_spell( k_spell_berserk, message );
			duration = t_combat_spell::k_duration_song_of_peace;
			break;
	};
	if (duration > 0 && this == m_battlefield.get_acting_creature())
		duration++;
	if (!effect.active)
	{
		m_active_spells.push_front( spell );
		memset( effect.source, 0, sizeof( effect.source ));
	}
	effect.active = true;
	effect.source[source] = true;
	effect.power = power;
	effect.duration = duration;
	if (spell == k_spell_berserk || spell == k_spell_hypnotize 
		|| spell == k_spell_short_hypnotism)
		change_active_spell_effect();
}
	
// ------------------------------------------------------------------------------
// change spell points
// ------------------------------------------------------------------------------
void t_combat_creature::change_spell_points( int amount )
{
	m_spell_points += amount;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_combat_creature::do_update_original_army( t_creature_array** armies )
{
	clear_all_spells( t_combat_action_message() );
	t_abstract_combat_creature::do_update_original_army( armies );
}

// ------------------------------------------------------------------------------
// transfer any transferable spells from the creature stack
// ------------------------------------------------------------------------------
void t_combat_creature::transfer_spells( t_creature_stack* stack )
{
	t_spell spell;
	for ( spell = t_spell( 0 ); spell < k_spell_count; enum_incr( spell ) )
	{
		// check if the spell is both an adventure spell and a combat spell
		if ( !spell_is( spell, k_spell_bit_combat, true ) )
			continue;

		if ( stack->is_spell_active( spell ) )
		{
			set_spell( spell, t_combat_action_message(), get_spell_power( spell ) );
			if (spell != k_spell_guardian_angel)	// immortality / guardian angel 					
				stack->set_spell( spell, false );	// is cleared when used, not at start.
		}
	}
}

// ------------------------------------------------------------------------------
// bonuses received by influence
// ------------------------------------------------------------------------------
t_creature_influence_bonus::t_creature_influence_bonus()
{
	artifact_defense_bonus = 0;
	artifact_luck_bonus = 0;
	artifact_morale_bonus = 0;
	artifact_move_bonus = 0;
	artifact_offense_bonus = 0;
	artifact_speed_bonus = 0;
	artifact_spell_cost_modifier = 0;
	binding = false;
	tactics_speed_bonus = 0;
	tactics_move_bonus = 0;
	offense_bonus = 0; // from offense skill
	defense_bonus = 0;  // from defense skill
	leadership_luck_bonus = 0;
	leadership_morale_bonus = 0;
	magic_resistance = 0;
}


// ------------------------------------------------------------------------------
// bonus + corresponding creature + position of influence item in other list
// ------------------------------------------------------------------------------
t_creature_influence::t_creature_influence( t_creature_influence_bonus const& bonus, 
		                                    t_combat_creature* new_creature )
					: t_creature_influence_bonus( bonus ), creature( new_creature )
{
	list_position = t_influence_list::iterator();
}

// ------------------------------------------------------------------------------
// add an influence to a creature
// ------------------------------------------------------------------------------
void t_combat_creature::add_influence( t_combat_creature* creature,
									   t_creature_influence_bonus const& bonus )
{
	t_creature_influence* influenced_ptr = new t_creature_influence( bonus, creature );
	t_creature_influence* influencing_ptr = new t_creature_influence( bonus, this );

	m_influenced_creatures.push_front( influenced_ptr );
	creature->m_influencing_creatures.push_front( influencing_ptr );
	influenced_ptr->list_position = creature->m_influencing_creatures.begin();
	influencing_ptr->list_position = m_influenced_creatures.begin();
}

// ------------------------------------------------------------------------------
// check what influence, if any, we have on another creature
// ------------------------------------------------------------------------------
bool t_combat_creature::check_influence( t_combat_creature* creature )
{
	// check tactics
	t_creature_influence_bonus bonus;
	int						   distance;
	t_hero*					   hero;

	hero = get_hero();
	if (hero == 0)
		return false;
	if (creature == this)
		return false;

	distance = get_edge_distance( *creature );
	check_artifact_influence( *this, *creature, distance, bonus );
	if (get_controller() == creature->get_controller()
		&& creature->get_creature_type() != k_creature_none )
	{
		bonus.tactics_speed_bonus = get_tactics_speed_bonus();
		bonus.offense_bonus = get_tactics_offense_bonus();
		bonus.defense_bonus = get_tactics_defense_bonus();
		bonus.leadership_luck_bonus = get_leadership_luck_bonus();
		bonus.leadership_morale_bonus = get_leadership_morale_bonus();
		bonus.tactics_move_bonus = get_tactics_move_bonus();
	}

	if (!bonus.abilities.empty()
		|| bonus.artifact_defense_bonus != 0
		|| bonus.artifact_luck_bonus != 0
		|| bonus.artifact_morale_bonus != 0
		|| bonus.artifact_move_bonus != 0
		|| bonus.artifact_offense_bonus != 0
		|| bonus.artifact_speed_bonus != 0
		|| bonus.artifact_spell_cost_modifier != 0
		|| !bonus.artifact_spell_immunities.empty()
		|| !bonus.artifact_spell_effects.empty()
		|| bonus.binding
		|| bonus.defense_bonus != 0
		|| bonus.leadership_luck_bonus != 0
		|| bonus.leadership_morale_bonus != 0
		|| bonus.magic_resistance != 0
		|| bonus.offense_bonus != 0
		|| bonus.tactics_move_bonus != 0
		|| bonus.tactics_speed_bonus != 0)
	{
		add_influence( creature, bonus );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------------------
// clear all bonuses and recalculate them based on influencing creatures
// ------------------------------------------------------------------------------
void t_combat_creature::check_influences()
{
	t_influence_list::iterator index = m_influencing_creatures.begin();
	t_influence_list::iterator end = m_influencing_creatures.end();
	t_creature_influence*      influence;
	int						   i;
	int						   artifact_defense_bonus = 0;
	int						   artifact_luck_bonus = 0;
	int						   artifact_morale_bonus = 0;
	int						   artifact_move_bonus = 0;
	int						   artifact_offense_bonus = 0;
	int						   artifact_speed_bonus = 0;
	t_spell_set				   spell_effects;
	t_hero*						hero;

	hero = get_hero();
	if (hero != 0)
	{
		artifact_morale_bonus = get_artifact_morale( *hero );
		artifact_luck_bonus = get_artifact_luck( *hero );
	}

	m_tactics_speed_bonus = 0;
	m_offense_bonus = 0;
	m_defense_added = 0;
	m_luck_bonus = 0;
	m_magic_resistance_bonus = 0;
	m_morale_bonus = 0;
	m_move_bonus = 0;
	m_artifact_spell_cost_modifier = 0;
	m_temporary_spell_immunities.clear();
	// reset abilities to normal ones without influences
	memcpy( m_has_ability, m_normally_has_ability, sizeof( m_has_ability ));
	clear_spell( k_spell_binding, t_combat_action_message() );
	for (; index != end; index++)
	{
		// add influences
		influence = index->get();
		// add abilities
		for (i = 0; i < influence->abilities.size(); ++i)
			m_has_ability[influence->abilities[i]] = true;
		for (i = 0; i < influence->artifact_spell_immunities.size(); ++i)
		{
			t_spell_immunity_map::iterator   index;
			t_artifact_spell_immunity const& immunity
				= influence->artifact_spell_immunities[i];

			index = m_temporary_spell_immunities.find( immunity.spell );
			if (index == m_temporary_spell_immunities.end())
				m_temporary_spell_immunities[immunity.spell] = immunity;
		}
		for (i = 0; i < influence->artifact_spell_effects.size(); ++i)
			spell_effects.set( influence->artifact_spell_effects[i] );

		if (influence->artifact_defense_bonus > artifact_defense_bonus)
			artifact_defense_bonus = influence->artifact_defense_bonus;
		if (influence->artifact_luck_bonus > artifact_luck_bonus)
			artifact_luck_bonus = influence->artifact_luck_bonus;
		if (influence->artifact_move_bonus > artifact_move_bonus)
			artifact_move_bonus = influence->artifact_move_bonus;
		if (influence->artifact_morale_bonus > artifact_morale_bonus)
			artifact_morale_bonus = influence->artifact_morale_bonus;
		if (influence->artifact_offense_bonus > artifact_offense_bonus)
			artifact_offense_bonus = influence->artifact_offense_bonus;
		if (influence->artifact_speed_bonus > artifact_speed_bonus)
			artifact_speed_bonus = influence->artifact_speed_bonus;
		if (influence->artifact_spell_cost_modifier < m_artifact_spell_cost_modifier)
			m_artifact_spell_cost_modifier = influence->artifact_spell_cost_modifier;
		if (influence->tactics_speed_bonus > m_tactics_speed_bonus)
			m_tactics_speed_bonus = influence->tactics_speed_bonus;
		if (influence->tactics_move_bonus > m_move_bonus)
			m_move_bonus = influence->tactics_move_bonus;
		if (influence->defense_bonus > m_defense_added)
			m_defense_added = influence->defense_bonus;
		if (influence->offense_bonus > m_offense_bonus)
			m_offense_bonus = influence->offense_bonus;
		if (influence->leadership_morale_bonus > m_morale_bonus)
			m_morale_bonus = influence->leadership_morale_bonus;
		if (influence->leadership_luck_bonus > m_luck_bonus)
			m_luck_bonus = influence->leadership_luck_bonus;
		if (influence->magic_resistance > m_magic_resistance_bonus)
			m_magic_resistance_bonus = influence->magic_resistance;
		if (influence->binding && influence->creature->get_controller() != get_controller())
			set_spell( k_spell_binding, t_combat_action_message() );
	}

	// update artifact spell effects
	t_spell spell;
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		// check if we lost a spell effect
		if (m_spells[spell].active && m_spells[spell].source[k_spell_source_artifact]
			&& !spell_effects[spell] )
		{
			m_spells[spell].source[k_spell_source_artifact] = false;
			if (   !m_spells[spell].source[k_spell_source_spell] 
				&& !m_spells[spell].source[k_spell_source_permanent] )
				clear_spell( spell, t_combat_action_message() );
		}
		if (!m_spells[spell].active && spell_effects[spell] )
			set_spell( spell, t_combat_action_message(), 0, k_spell_source_artifact );
	}

	m_defense_added += artifact_defense_bonus;
	m_luck_bonus += artifact_luck_bonus;
	m_morale_bonus += artifact_morale_bonus;
	m_move_bonus += artifact_move_bonus;
	m_offense_bonus += artifact_offense_bonus;
	m_tactics_speed_bonus += artifact_speed_bonus;
}

// ------------------------------------------------------------------------------
// clear all links to other creatures
// ------------------------------------------------------------------------------
void t_combat_creature::clear_influence()
{
	t_influence_list::iterator index = m_influencing_creatures.begin();
	t_influence_list::iterator end	 = m_influencing_creatures.end();
	t_creature_influence*      influence;

	for (; index != end; index++)
	{
		influence = index->get();
		influence->creature->m_influenced_creatures.erase( influence->list_position );
	}

	index = m_influenced_creatures.begin();
	end   = m_influenced_creatures.end();
	for (; index != end; index++)
	{
		influence = index->get();
		influence->creature->m_influencing_creatures.erase( influence->list_position );
		influence->creature->check_influences();
	}
	m_influencing_creatures.clear();
	m_influenced_creatures.clear();
	m_influence_set = false;
	m_in_water = false;
	m_terrain_defense = 0;
}

// ------------------------------------------------------------------------------
// find edge-to-edge distance of two creatures
// given that the current creature is in a particular cell
// ------------------------------------------------------------------------------
int	t_combat_creature::get_edge_distance( t_combat_object_base const&	object,
										  t_map_point_2d const&			cell_point ) const
{
	t_map_point_2d center = get_footprint_center( cell_point );
	int            distance;

	// get distance between centers
	center -= object.get_footprint_center();
	center.row *= center.row;
	center.column *= center.column;
	distance = sqrt( (double)(center.row + center.column) ) + 0.5;
	// subtract half the footprint size of each creature
	distance -= get_footprint_size() << (k_battlefield_subcell_shift - 1);
	distance -= object.get_footprint_size() << (k_battlefield_subcell_shift - 1);
	// add 1/2 cell to round to nearest
	distance += k_battlefield_subcells_per_cell / 2;
	return distance >> k_battlefield_subcell_shift;
}


	// check if creature is in water / quicksand
static bool check_water( t_combat_creature const& creature )
{
	t_battlefield&				battlefield = creature.get_battlefield();
	t_combat_footprint const& footprint = creature.get_footprint();
	t_map_point_2d			  point = creature.get_cell_position();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	t_battlefield_cell const* cell;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			cell = &battlefield.get_cell( new_point );
			if (cell->is_quicksand() || cell->get_terrain_type() == k_terrain_water_river)
				return true;
		}
	}
	return false;
}

	// check if creature is in native brush
static bool check_brush_bonus( t_combat_creature const& creature )
{
	t_battlefield&				battlefield = creature.get_battlefield();
	t_combat_footprint const& footprint = creature.get_footprint();
	t_map_point_2d			  point = creature.get_cell_position();
	int                       footprint_size = footprint.get_size();
	t_map_point_2d            offset;
	t_map_point_2d            new_point;
	int const*				  ptr = footprint.get();
	t_battlefield_cell const* cell;
	
	for (offset.row = 0; offset.row < footprint_size; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size; offset.column++)
		{
			if (!(*(ptr++)))
				continue;
			new_point = point + offset;
			cell = &battlefield.get_cell( new_point );
			if (cell->has_brush() 
				&& creature.is_native_terrain( cell->get_terrain_type()))
				return true;
		}
	}
	return false;
}


// ------------------------------------------------------------------------------
// set influence for a creature on other creatures - and their influence on it
// ------------------------------------------------------------------------------
void t_combat_creature::set_influence()
{
	if (m_influence_set)
		return;

	t_combat_creature_iterator index = m_battlefield.creatures_begin();
	t_combat_creature_iterator end   = m_battlefield.creatures_end();
	t_combat_creature*         creature;

	m_influence_set = true;
	for (; index != end; index++)
	{
		creature = index->get();
		if (creature->get_number() == 0)
			continue;
		if (creature == this)
			continue;

		if (check_influence( creature ))
			creature->check_influences();
		creature->check_influence( this );
	}
	check_influences();
	m_in_water = check_water( *this );
	if (check_brush_bonus( *this ))
		m_terrain_defense = 25;
	else
		m_terrain_defense = 0;
}

// ------------------------------------------------------------------------------
// get current bonus to defense
// ------------------------------------------------------------------------------
float t_combat_creature::get_defense_bonus( bool ranged ) const
{
	float result = m_defense_bonus[ranged];
	
	if ( is_in_tower())
		result *= 2.0f;
	if (ranged && is_active( k_spell_blur ))
		result *= 1.5f;
	result = result * (m_defense_added + 100) / 100;
	if (m_defending)
	{
		result *= 1.25f;
		if (is_active( k_spell_divine_protection ))
			result *= 1.50f;
	}
	if (has_ability( k_ability_sea_bonus) && m_battlefield.is_sea_battle())
		result *= 1.5f;
	if (is_active( k_spell_holy_armor ))
		result *= 1.25f;
	if (is_active( k_spell_stoneskin ))
		result *= 1.25f;
	if (m_terrain_defense != 0)
		result = result * (100 + m_terrain_defense) / 100;
	if (m_in_water)
		result *= 0.8f;
	if (is_active( k_spell_acid ))
		result *= 0.8f;
	if (is_active( k_spell_aging ))
		result *= 0.8f;
	if (is_active( k_spell_evil_hour ))
		result *= 0.8f;
	if (is_active( k_spell_ray_of_suffering))
		result *= 0.8f;
	if (is_active( k_spell_gas ))
		result *= 0.85f;
	return result;
}

// ------------------------------------------------------------------------------
// get # of times the creature strikes when attacking
// ------------------------------------------------------------------------------
int t_combat_creature::get_attacks( bool ranged ) const
{
	int base = 1;

	if (is_active( k_spell_quickness ))
		base++;
	if (is_active( k_spell_reflexes ))
		base++;
	if (ranged)
	{
		if (has_ability( k_ability_shoots_twice ))
			++base;
		if (has_ability( k_ability_extra_ranged_attack ))
			++base;
	}
	else
	{
		if (has_ability( k_ability_strikes_twice ))
			++base;
		if (has_ability( k_ability_extra_melee_attack ))
			++base;
	}
	return base;
}


// -----------------------------------------------------------------------------
// get chance of a spell affecting a creature
// ------------------------------------------------------------------------------

int t_combat_creature::get_spell_chance( t_combat_creature const& target, t_spell spell ) const
{
	int result;

	if (!can_affect( *this, spell, target ))
		return 0;
	if (!can_resist( spell ))
		return 100;
	if (has_ability( k_ability_ignore_wards ))
		result = 100 - target.get_magic_resistance( k_spell_none );
	else
		result = 100 - target.get_magic_resistance( spell );
	return result;
}


// -----------------------------------------------------------------------------
// get resistance to hostile spells
// ------------------------------------------------------------------------------

int t_combat_creature::get_magic_resistance( t_spell spell ) const
{
	int result = 100 - t_abstract_combat_creature::get_magic_resistance( spell );

	if (has_ability( k_ability_spell_vulnerability ))
		return 0;
	// add any bonus for artifacts
	if (m_magic_resistance_bonus > 0)
		result = result * (100 - m_magic_resistance_bonus) / 100;

	if (result <= 0)
		return 100;

	return 100 - result;
}

int t_combat_creature::get_magic_resistance( t_combat_creature const& caster, t_spell spell ) const
{
	if (caster.has_ability( k_ability_ignore_wards ))
		return get_magic_resistance( k_spell_none );
	return get_magic_resistance( spell );
}

// -----------------------------------------------------------------------------
// clear a spell from a creature
// ------------------------------------------------------------------------------
void t_combat_creature::clear_spell( t_spell spell, t_combat_action_message const & message )
{
	t_spell_effect& effect = m_spells[spell];

	if (!effect.active || !can_remove( spell ))
		return;

	int				old_hits = get_hit_points();

	effect.active = false;
	memset( effect.source, 0, sizeof( effect.source ));
	m_active_spells.remove( spell );

	switch (spell)
	{
		case k_spell_animate_dead:
		case k_spell_death_call:
		case k_spell_illusionary_ally:
		case k_spell_clone:
		case k_spell_raise_skeletons:
		case k_spell_raise_ghost:
		case k_spell_raise_vampires:
			m_number = 0;
			m_wounds = 0;
			assert( message.is_displayable() );
			animate_death( false, message );
			return;

		case k_spell_frozen:
			set_color( 0, 0, 256, 256 );
			break;

		case k_spell_magic_leech:
			m_magic_leech_client = 0;
			break;

		case k_spell_martyr:
			set_martyr_protector( 0 );
			break;

		case k_spell_berserk:
		case k_spell_hypnotize:
		case k_spell_short_hypnotism:
			change_active_spell_effect();
			break;
	}

	int new_hits = get_hit_points();

	if (new_hits < old_hits)
	{
		m_wounds = m_wounds * new_hits / old_hits;
	}
}

// -----------------------------------------------------------------------------
// get and adjust spell damage
// ------------------------------------------------------------------------------
int t_combat_creature::get_spell_damage( t_combat_creature const& target, t_spell spell,
										 int damage ) const
{
	if (!can_affect( *this, spell, target ))
		return 0;
	return target.modify_spell_damage( this, damage, spell );
}


// -----------------------------------------------------------------------------
// reduce duration of a spell
// -----------------------------------------------------------------------------
void t_combat_creature::decrement_durations()
{
	t_spell_iterator next;
	t_spell			 spell;

	next = m_active_spells.begin();
	while (next != m_active_spells.end())
	{
		spell = *next;
		++next;
		assert( is_active( spell ));
		if (m_spells[spell].duration == 0)
			continue;
		if (--m_spells[spell].duration > 0)
			continue;
		clear_spell( spell, t_combat_action_message() );
	}
}


// -----------------------------------------------------------------------------
// return true if creature can retaliate against an attack
// -----------------------------------------------------------------------------
bool t_combat_creature::can_retaliate( t_combat_creature const& attacker, bool ranged, 
									   int turns ) const
{
	if (has_retaliated() && turns < 2)
		return false;
	
	if (is_active(k_spell_frozen) && get_effect_power( k_spell_frozen ) >= turns)
		return false;
	if (is_active(k_spell_stun) && get_effect_power( k_spell_stun ) >= turns )
		return false;

	if (ranged)
	{
		if (!m_battlefield.can_shoot(*this))
			return false;
	}
	else
	{
		if (attacker.has_ability( k_ability_no_retaliation))
			return false;
		if (attacker.get_attack_range() > get_attack_range()
			&& get_edge_distance( attacker ) > get_attack_range() )
			return false;
		if (attacker.is_active( k_spell_fear )
			&& can_affect( k_spell_fear_effect, *this )
			&& !has_ability( k_ability_death_protection ))
			return false;
		if (attacker.has_ability( k_ability_panic ) 
			&& can_affect( k_spell_panic, *this )
			&& !has_ability( k_ability_death_protection ))
			return false;
	}
	return true;
}

// -----------------------------------------------------------------------------
// return last spell cast, if possible
// -----------------------------------------------------------------------------
t_spell t_combat_creature::get_default_spell() const
{
	if (m_default_spell == k_spell_none)
		return k_spell_none;
	if (get_spell_cost( m_default_spell ) > m_spell_points)
		return k_spell_none;
	if (!is_single_target( m_default_spell ))
		return k_spell_none;
	return m_default_spell;
}

static t_external_string const k_combat_action_martyr_protected_effect( "combat_action.martyr_protected_effect" );

// -----------------------------------------------------------------------------
// add damage and check for luck
// -----------------------------------------------------------------------------
int t_combat_creature::add_damage( int amount, t_combat_creature* source,
								   bool check_luck, bool permanent,
								   t_combat_action_message const & message )
{
	if (amount < 1)
		return 0;

	// check if the Guardian Robe cancelled the damage
	if (m_guardian_robe_strength > 0)
	{
		--m_guardian_robe_strength;
		m_robe_blocked_damage = true;
		return 0;
	}

	m_new_damage_is_permanent = permanent;
	if (check_luck)
	{
		if (random(10) < get_luck())
		{
			m_got_good_luck = true;
			amount = amount * 2 / 3;
			if (amount < 1)
				amount = 1;
		}
		if (random(10) < -get_luck())
		{
			m_got_bad_luck = true;
			amount += amount >> 1;
		}
	}

	int		  total_hits;
	int		  damage_absorbed = 0;
	t_handler martyr_action;

	// check if damage should be transferred due to the Martyr spell
	if (is_active( k_spell_martyr ) && m_martyr_protector->get_number() > 0)
	{
		damage_absorbed = amount;
		total_hits = m_martyr_protector->get_total_hits() 
			         - m_martyr_protector->get_new_damage();
		if (total_hits < 0)
			total_hits = 0;
		if (total_hits < damage_absorbed)
			damage_absorbed = total_hits;
		amount -= damage_absorbed;
		if (damage_absorbed > 0)
		{
			t_combat_action_message message( *this, k_combat_action_martyr_protected_effect);
			m_battlefield.start_animation( *this, k_spell_effect_martyr_client, message );
			martyr_action = new t_martyr_action( this, m_martyr_protector,
				                                 damage_absorbed, permanent );
		}
		if (amount == 0)
			m_new_damage_is_permanent = false;
	}
	
	total_hits = get_total_hits();

	if (m_new_damage + amount > total_hits)
		amount = total_hits - m_new_damage;
	if (amount < 0)
		amount = 0;
	m_new_damage += amount;

	if ( !m_new_damage_message.is_displayable() || m_new_damage_message.get_priority() <= message.get_priority() )
	{
		m_new_damage_message = message;
	}

	amount += damage_absorbed;
	if (is_active( k_spell_pain_mirror ) && source != 0 
		&& can_affect( k_spell_pain_mirror, *source))
		m_battlefield.add_action( new t_pain_mirror_action( this, source, amount,
		                                                    permanent ), true );
	// add martyr action after pain mirror, so that martyr happens first
	if (martyr_action != 0)
		m_battlefield.add_action( martyr_action, true );
	return amount;
}

// -----------------------------------------------------------------------------
// check who moves first
// -----------------------------------------------------------------------------
void t_combat_creature::check_morale()
{
	m_morale_roll = random(0,9);
	m_got_good_morale = false;
	m_got_bad_morale = false;
	m_morale_shown = false;
}

// -----------------------------------------------------------------------------
// check good morale
// -----------------------------------------------------------------------------
bool t_combat_creature::got_good_morale() const
{
	if (m_morale_shown)
		return m_got_good_morale;
	return m_morale_roll < get_morale();
}

// -----------------------------------------------------------------------------
// check bad morale
// -----------------------------------------------------------------------------
bool t_combat_creature::got_bad_morale() const
{
	return m_got_bad_morale;
}

// -----------------------------------------------------------------------------
// check who moves first
// returns a result from -2100 to 2100, roughly.
// -----------------------------------------------------------------------------
int t_combat_creature::get_adjusted_speed() const
{
	int result = get_speed();

	if (!got_bad_morale())
		result += 1000;
	if (got_good_morale())
		result += 1000;
	if (is_waiting())
		result = -result;
	return result;
}

// -----------------------------------------------------------------------------
// check who moves first
// -----------------------------------------------------------------------------
bool t_combat_creature::moves_before( t_combat_creature const& creature,
									  bool last_was_defender ) const
{
	int speed_difference = get_adjusted_speed() - creature.get_adjusted_speed();

	if (speed_difference < 0)
		return false;
	if (speed_difference > 0)
		return true;
	if (get_controller() == creature.get_controller())
		return false;
	return get_controller() != last_was_defender;
}

// -----------------------------------------------------------------------------
// show morale
// -----------------------------------------------------------------------------
void t_combat_creature::pre_action_flinch( t_combat_action_message const & message )
{
	if (is_active( k_spell_frozen ))
		return;

	t_play_combat_animation_ptr animation;

	animation = dynamic_cast<t_play_combat_animation*>(get_animation().get());

	// check if the target is currently playing an animation
	if (animation != 0 && animation->get_action() != k_combat_actor_action_wait
		&& animation->get_action() != k_combat_actor_action_fidget)
		return;

	t_battlefield&				battlefield = m_battlefield;
	t_combat_creature_handler	handler;

	handler = bound_handler( battlefield, &t_battlefield::end_animation );
	animation = new t_play_combat_animation( *this, get_current_direction(),
			                                 k_combat_actor_action_flinch, false );
	animation->set_end_handler( handler );
	animation->display_action_message( message );
	set_animation( animation );
}

static t_external_string const k_combat_action_bad_morale( "combat_action.bad_morale" );
static t_external_string const k_combat_action_good_morale( "combat_action.good_morale" );

// -----------------------------------------------------------------------------
// show morale
// -----------------------------------------------------------------------------
bool t_combat_creature::show_morale_animation()
{
	if (m_morale_shown)
		return false;
	m_morale_shown = true;
	if (9 - m_morale_roll < -get_morale())
	{
		t_combat_action_message message( *this, k_combat_action_bad_morale );

		m_got_bad_morale = true;
		pre_action_flinch( message );
		m_battlefield.start_pre_action_animation( *this, k_spell_sorrow, message );
		return true;
	}
	if (m_morale_roll < get_morale())
	{
		t_combat_action_message message( *this, k_combat_action_good_morale );

		m_got_good_morale = true;
		m_battlefield.start_pre_action_animation( *this, k_spell_spiritual_fervor, message );
		set_animation( k_combat_actor_action_fidget, message );
		return true;
	}
	return false;
}

// -----------------------------------------------------------------------------
// do a healing, but allow resurrection
// -----------------------------------------------------------------------------
void t_combat_creature::resurrect( int healed, bool show_number )
{
	int hit_points = get_hit_points();
	int revived    = (healed - m_wounds + hit_points - 1) / hit_points;
	int dead       = m_creature_stack->get_number() - m_number - m_permanent_deaths;

	if (dead < 0)
		dead = 0;
	if (revived < 0)
		revived = 0;
	if (revived > dead)
		revived = dead;
	healed -= revived * hit_points; // result can be negative
	if (healed > m_wounds)          // can't heal more damage than we've taken
		healed = m_wounds;
	m_wounds -= healed;
	m_number += revived;
	assert( m_wounds < hit_points );
	healed += revived * hit_points;  // re-calculate the actual amount healed
	if (healed > 0 && show_number)
		m_battlefield.add_text( *this, format_string( "+%i", healed ), 
		                               get_message_color(), k_message_icon_heal );
	if (m_label->get_list_position()._Mynode() == NULL)
		m_battlefield.place_object( m_label, m_label->get_position() );
	m_battlefield.invalidate( *m_label );
}

static t_external_string const k_combat_action_guardian_robe_effect( "combat_action.guardian_robe_effect" );

// -----------------------------------------------------------------------------
// check if creature was protected by the robe of the guardian. If so, play
// the little animation
// -----------------------------------------------------------------------------
bool t_combat_creature::check_guardian_robe()
{
	if (m_robe_blocked_damage)
	{
		t_combat_action_message message( *this, k_combat_action_guardian_robe_effect );
		m_battlefield.start_animation( *this, k_spell_effect_guardian_robe, message );
		m_robe_blocked_damage = false;
		return true;
	}

	return false;
}


// -----------------------------------------------------------------------------
// check if creature should flinch
// -----------------------------------------------------------------------------
bool t_combat_creature::check_flinch( t_combat_action_message const & message )
{
	check_guardian_robe();

	if (m_new_damage <= 0)
		return false;
	if (m_new_damage >= get_total_hits())
		return false;
	set_animation( k_combat_actor_action_flinch, message );
	return true;
}

// -----------------------------------------------------------------------------
// kill creatures
// -----------------------------------------------------------------------------
void t_combat_creature::kill( int amount )
{
	if (amount > m_number)
		amount = m_number;
	m_number -= amount;
	m_guardian_angel_limit = amount;
	if (m_number == 0)
		m_wounds = 0;
	else if (is_active( k_spell_guardian_angel ))
		use_guardian_angel();
}

// -----------------------------------------------------------------------------
// devour and/or disintegrate a creature
// -----------------------------------------------------------------------------
void t_combat_creature::kill_permanently( int amount, t_combat_action_message const & message )
{
	t_battlefield& battlefield = m_battlefield;

	if (amount > m_number)
		amount = m_number;
	// check if the Robe of the Guardian protects us
	if (m_guardian_robe_strength > 0)
	{
		// if this is part of another blocked attack, don't count it twice,
		// or show it twice.
		if (m_robe_blocked_damage)
			return;

		t_combat_action_message robe_message( *this, k_combat_action_guardian_robe_effect );

		--m_guardian_robe_strength;
		battlefield.start_animation( *this, k_spell_effect_guardian_robe, robe_message );
		return;
	}
	increase_permanent_death_count( amount );
	m_number -= amount;
	battlefield.add_text( *this, format_string( "-%i", amount ), 
		                           get_message_color(), k_message_icon_death );
	m_guardian_angel_limit = m_number;
	if (m_number > 0)
	{
		set_animation( k_combat_actor_action_flinch, message );
		if (m_spells[k_spell_guardian_angel].active)
			use_guardian_angel();
	}
	else
		animate_death( true, message );
}

bool t_combat_creature::is_underlay() const
{
	return m_is_underlay;
}

// -----------------------------------------------------------------------------
// check if creature blocks an attack
// -----------------------------------------------------------------------------
void t_combat_creature::check_blocking() 
{
	m_blocking_attack = false;
	if (is_active( k_spell_frozen) || is_active( k_spell_stun ))
		return;
	if (m_block_chance <= 0)
		return;
	if (random(100) >= m_block_chance)
		return;
	m_blocking_attack = true;
}

// -----------------------------------------------------------------------------
// set a creature as an underlay
// -----------------------------------------------------------------------------
void t_combat_creature::set_underlay( bool underlay )
{
	m_is_underlay = underlay;
	if (get_list_position()._Mynode() != NULL)
		m_battlefield.set_drawing_order( *this );
}

// -----------------------------------------------------------------------------
// check control of creature
// -----------------------------------------------------------------------------
bool t_combat_creature::get_controller() const
{
	if (is_active( k_spell_hypnotize ) || is_active( k_spell_short_hypnotism))
		return !m_belongs_to_defender;
	return m_belongs_to_defender;
}


// -----------------------------------------------------------------------------
// start a creature fading in
// -----------------------------------------------------------------------------
void t_combat_creature::fade_in( t_combat_action_message const & message )
{
	t_abstract_combat_object::fade_in( m_battlefield, m_normal_alpha, message );
}

// -----------------------------------------------------------------------------
// start a creature fading out
// -----------------------------------------------------------------------------
void t_combat_creature::fade_out( t_combat_action_message const & message )
{
	t_handler handler;

	m_normal_alpha = 0;
	if (is_summoned())
		handler = bound_handler( *this, &t_combat_creature::remove_from_map );
	t_abstract_combat_object::fade_out( m_battlefield, handler, message );
}

// -----------------------------------------------------------------------------
// start a creature fading out
// -----------------------------------------------------------------------------
void t_combat_creature::fade_out( t_handler handler, t_combat_action_message const & message )
{
	t_abstract_combat_object::fade_out( m_battlefield, handler, message );
}


// -----------------------------------------------------------------------
// battlefield class
// set transparency level for a creature
// -----------------------------------------------------------------------
void t_combat_creature::set_alpha( t_battlefield& battlefield, int alpha ) 
{
	assert( alpha >= 0 && alpha <= 15 );
	if (alpha == get_alpha())
		return;
	t_combat_actor::set_alpha( m_battlefield, alpha );

	t_combat_label* label = get_label();

	if (label != 0)
		label->set_alpha( m_battlefield, alpha );
}

static void set_shadow( bool arg, char const* name, t_combat_object_ptr& shadow, 
					    t_combat_creature& creature )
{
	t_battlefield&	battlefield = creature.get_battlefield();

	if (!arg)
	{
		if (shadow != 0)
		{
			battlefield.remove_object( shadow );
			shadow = 0;
		}
		return;
	}
	if (shadow != 0)
		return;

	std::string						shadow_name = name;
	t_combat_object_model_cache	cache;

	shadow_name += ".";
	shadow_name += format_string( "%i", creature.get_footprint_size() );
	cache = get_combat_object_model_cache( shadow_name, battlefield.get_model_scale() );
	shadow = new t_stationary_combat_object( battlefield, cache );
	battlefield.place_object( shadow, creature.get_position() );
	battlefield.invalidate( *shadow );

}

// -----------------------------------------------------------------------------
// create a selection shadow
// -----------------------------------------------------------------------------
void t_combat_creature::set_selection_shadow( bool arg )
{
	set_shadow( arg, "target_shadow", m_selection_shadow, *this );
}


// -----------------------------------------------------------------------------
// create a selection shadow
// -----------------------------------------------------------------------------
void t_combat_creature::set_active_shadow( bool arg )
{
	set_shadow( arg, "active_shadow", m_active_shadow,  *this  );
}

// -----------------------------------------------------------------------------
// get range to a square
// -----------------------------------------------------------------------------
int t_combat_creature::get_edge_distance( t_map_point_2d const& cell ) const
{
	return get_edge_subcell_distance( get_cell_center( cell ) ) >> k_battlefield_subcell_shift;
}

// -----------------------------------------------------------------------------
// get range to a square
// -----------------------------------------------------------------------------
int t_combat_creature::get_edge_subcell_distance( t_map_point_2d const& target_center ) const
{
	int             distance;
	t_map_point_2d	delta;

	delta = target_center - get_footprint_center();
	distance = sqrt( (double)(delta.row * delta.row + delta.column * delta.column) ) + 0.5;
	distance -= get_footprint_size() << (k_battlefield_subcell_shift - 1);
	return distance;
}

// -----------------------------------------------------------------------
// get current animated action, if any 
// -----------------------------------------------------------------------
t_combat_actor_action_id t_combat_creature::get_animation_action() const
{
	t_play_combat_animation* animation;

	animation = dynamic_cast<t_play_combat_animation*>(get_animation().get());

	// check if the target is currently playing an animation
	if (animation == 0)
		return k_combat_actor_action_wait;
	return animation->get_action();
}

// -----------------------------------------------------------------------
// get current animated action, if any 
// -----------------------------------------------------------------------
bool t_combat_creature::is_busy() const
{
	t_combat_actor_action_id action = get_animation_action();

	return (action != k_combat_actor_action_wait && action != k_combat_actor_action_fidget);
}

// -----------------------------------------------------------------------
// run 2 actions
// -----------------------------------------------------------------------
namespace
{
	class t_sequence_actions : public t_handler_base_1<t_combat_creature&>
	{
	public:
		t_sequence_actions( t_combat_creature_handler action_1, 
			                t_combat_creature_handler action_2 );
		void operator()( t_combat_creature& creature );
	protected:
		t_combat_creature_handler m_action_1;
		t_combat_creature_handler m_action_2;
	};

	inline 
	t_sequence_actions::t_sequence_actions( t_combat_creature_handler action_1,
		                                    t_combat_creature_handler action_2 )
	{
		m_action_1 = action_1;
		m_action_2 = action_2;
	}

	void t_sequence_actions::operator ()( t_combat_creature& creature )
	{
		m_action_1( creature );
		m_action_2( creature );
	}
}

	
// -----------------------------------------------------------------------
// append an action to the existing animation
// -----------------------------------------------------------------------
void t_combat_creature::append_handler( t_combat_creature_handler new_action )
{
	t_play_combat_animation* animation;

	animation = dynamic_cast<t_play_combat_animation*>(get_animation().get());
	assert( animation != 0 );

	t_combat_creature_handler old_action = animation->get_end_handler();

	// if the old action is not null, set up a call back that will call it before
	// calling this function.
	if (old_action.get() != 0)
		new_action = new t_sequence_actions( old_action, new_action );
	animation->set_end_handler( new_action );
	return;
}


// -----------------------------------------------------------------------
// clear binding from other creatures
// -----------------------------------------------------------------------
void t_combat_creature::clear_bindings()
{
	t_influence_list::iterator index = m_influenced_creatures.begin();
	t_influence_list::iterator last;
	t_influence_list::iterator end = m_influenced_creatures.end();
	t_creature_influence*      influence;

	while (index != end)
	{
		influence = index->get();
		last = index;
		index++;
		if (!influence->binding)
			continue;
		influence->creature->m_influencing_creatures.erase( influence->list_position );
		influence->creature->check_influences();
		m_influenced_creatures.erase( last );
	}
}

// -----------------------------------------------------------------------
// set binding on another creature
// -----------------------------------------------------------------------
void t_combat_creature::set_binding( t_combat_creature& target, t_combat_action_message const & message )
{
	t_influence_list::iterator index = m_influenced_creatures.begin();
	t_influence_list::iterator end = m_influenced_creatures.end();

	for (; index != end; index++)
		if (index->get()->creature.get() == &target && index->get()->binding)
			break;
	if (index != end)
		return;

	clear_bindings();
	if (!target.is_active( k_spell_binding ))
	{
		m_battlefield.start_animation( target, k_spell_binding, message );
		target.set_spell( k_spell_binding, message );
	}

	t_creature_influence_bonus binding;

	binding.binding = true;
	add_influence( &target, binding );
}


// -----------------------------------------------------------------------
// handle the resurrection spell
// play death animation in reverse
// -----------------------------------------------------------------------
void t_combat_creature::show_resurrection( t_combat_action_message const & message )
{
	t_play_combat_animation_ptr  animation;
	t_combat_creature_handler    handler;

	assert( get_current_action() == k_combat_actor_action_die );
	handler = bound_handler( m_battlefield, &t_battlefield::end_animation );
	animation = new t_play_combat_animation( *this, 
			                                 get_current_direction(),
											 k_combat_actor_action_die );
	animation->set_end_handler( handler );
	animation->set_reverse( true );
	animation->display_action_message( message );
	set_animation( animation );
	set_underlay( false );
	if (m_label->get_list_position()._Mynode() == NULL)
		m_battlefield.place_object( m_label, m_label->compute_position() );
	set_influence();
}

// -----------------------------------------------------------------------
// use guardian angels
// -----------------------------------------------------------------------
void t_combat_creature::show_angels( t_window*, int number )
{
	m_battlefield.add_text( *this, format_string( "+%i", number ), 
		                           get_message_color(), k_message_icon_angel );
	m_battlefield.update_state();
}

static t_external_string const k_combat_action_guardian_angel_effect( "combat_action.guardian_angel_effect" );

// -----------------------------------------------------------------------
// use guardian angels
// -----------------------------------------------------------------------
void t_combat_creature::use_guardian_angel()
{
	int				 number = m_guardian_angel_limit;
	int              limit  = get_salvageable_bodies();
	t_window_handler handler;

	t_combat_action_message message( *this, k_combat_action_guardian_angel_effect );

	if (number > m_spells[k_spell_guardian_angel].power)
		number = m_spells[k_spell_guardian_angel].power;
	if (number > limit)
		number = limit;
	m_number += number;
	m_spells[k_spell_guardian_angel].power -= number;
	if (m_spells[k_spell_guardian_angel].power == 0)
		clear_spell( k_spell_guardian_angel, message );
	handler = add_2nd_argument( bound_handler( *this, &t_combat_creature::show_angels ),
		                        number );
	m_battlefield.start_animation( *this, k_spell_guardian_angel, handler, message );
}

// -----------------------------------------------------------------------
// begin rebirth
// -----------------------------------------------------------------------
void t_combat_creature::begin_rebirth()
{
	if (is_active( k_spell_guardian_angel ))
	{
		t_combat_action_message message( *this, k_combat_action_guardian_angel_effect );
		use_guardian_angel();
		show_resurrection( message );
		m_creature_stack->set_spell( k_spell_guardian_angel, false );
		return;
	}

	t_combat_spell_ptr rebirth = create_combat_spell( m_battlefield, k_spell_rebirth );

	rebirth->set_caster( this );
	rebirth->begin_casting();
}

// -----------------------------------------------------------------------
// handle death animation
// -----------------------------------------------------------------------
void t_combat_creature::remove_from_map()
{
	clear_influence();
	clear_martyr_clients();
	m_battlefield.remove_object( get_label() );
	m_battlefield.remove_object( this );
	m_animation = 0;
}

// -----------------------------------------------------------------------
// handle death animation
// -----------------------------------------------------------------------
void t_combat_creature::animate_death( bool show_animation, t_combat_action_message const & message )
{
	t_battlefield& battlefield = m_battlefield;

	if (get_current_action() != k_combat_actor_action_die && show_animation)
	{
		t_combat_creature_handler handler;

		handler = bound_handler( battlefield, &t_battlefield::end_animation );
		set_animation( k_combat_actor_action_die, handler, message );
	}

	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (is_active( spell ) && !is_summoning( spell ) && can_remove( spell ) 
			&& spell != k_spell_sacred_ground && spell != k_spell_guardian_angel )
			clear_spell( spell, message );
	}

	battlefield.remove_object( get_label() );
	if (can_use_rebirth())
	{
		t_handler handler;

		handler = bound_handler( *this, &t_combat_creature::begin_rebirth );
		battlefield.add_action( handler, true );
	}
	else
	{
		if (is_summoned() || get_salvageable_bodies() == 0)
			fade_out( message );
	}
	clear_influence();
	clear_martyr_clients();
	change_active_spell_effect();
}

// -----------------------------------------------------------------------
// check if creature is too afraid to attack a target
// -----------------------------------------------------------------------
bool t_combat_creature::check_cowardice( t_combat_creature const& target ) const
{
	if (!is_active( k_spell_cowardice ))
		return false;
	return target.get_total_hits() >= get_total_hits();
}

// -----------------------------------------------------------------------
// get total hit points of target
// -----------------------------------------------------------------------
int t_combat_creature::get_total_hits(bool include_guardian_angel, t_spell ignore_effect) const
{
	int result = (m_number * get_hit_points(ignore_effect)) - m_wounds;

	if (consider_active(k_spell_celestial_armor, k_spell_none, ignore_effect ))
		result += m_spells[k_spell_celestial_armor].power;
	if (consider_active(k_spell_life_drain, k_spell_none, ignore_effect ))
		result += m_spells[k_spell_life_drain].power;

	if (include_guardian_angel && m_spells[k_spell_guardian_angel].active)
	{
		result += m_spells[k_spell_guardian_angel].power * get_hit_points(ignore_effect);
	}

	return result;
}


// -----------------------------------------------------------------------
// get square where a missile will impact
// -----------------------------------------------------------------------
t_map_point_2d
t_combat_creature::get_impact_point( t_map_point_2d const& source_cell,
									 t_map_point_2d const& target_cell ) const
{
	t_map_point_2d delta = source_cell - target_cell;
	t_map_point_2d result = target_cell;
	t_map_point_2d new_point;
	t_map_point_2d offset(0,0);
	int			   divisor;

	divisor = abs( delta.row );
	if (divisor < abs( delta.column ))
		divisor = abs( delta.column );
	// skip any cells behind the creature
	while (result != source_cell && m_battlefield.get_creature( result ) != this)
	{
		offset += delta;
		result = target_cell + offset / divisor;
	}
	// find first cell which contains the creature along the flight path
	while (result != source_cell)
	{
		offset += delta;
		new_point = target_cell + offset / divisor;
		if (m_battlefield.get_creature( new_point ) != this)
			break;
		result = new_point;
	}
	return result;
}



// -----------------------------------------------------------------------
// add to list of martyr clients
// -----------------------------------------------------------------------
void t_combat_creature::add_martyr_client( t_combat_creature_ptr creature )
{
	m_martyr_clients.push_back( creature );
}

// -----------------------------------------------------------------------
// clear all martyr clients
// -----------------------------------------------------------------------
void t_combat_creature::clear_martyr_clients()
{
	t_combat_creature_ptr client;

	while (!m_martyr_clients.empty())
	{
		client = m_martyr_clients.front();
		client->clear_spell( k_spell_martyr, t_combat_action_message() );
	}
}

// -----------------------------------------------------------------------
// remove from list of martyr clients
// -----------------------------------------------------------------------
void t_combat_creature::remove_martyr_client( t_combat_creature_ptr creature )
{
	m_martyr_clients.remove( creature );
}

// -----------------------------------------------------------------------
// remove from list of martyr clients
// -----------------------------------------------------------------------
void t_combat_creature::set_martyr_protector( t_combat_creature_ptr creature )
{
	if (m_martyr_protector != 0)
		m_martyr_protector->remove_martyr_client( this );
	m_martyr_protector = creature;
	if (m_martyr_protector != 0)
		m_martyr_protector->add_martyr_client( this );
}

// -----------------------------------------------------------------------
// Choose direction to face while waiting
// -----------------------------------------------------------------------
t_direction t_combat_creature::get_wait_direction( t_direction				current_direction,
												   t_map_point_2d const&	cell_point ) const
{
	t_direction direction = current_direction;

	if ((direction & 1) != 0)
	{
		// find the closest enemy
		t_battlefield&						battlefield = m_battlefield;
		t_combat_creature_list::iterator	index = battlefield.creatures_begin();
		t_combat_creature_list::iterator	end   = battlefield.creatures_end();
		int									distance;
		int									closest_distance;
		int									difference;
		int									angle;
		int									bearing;
		int									smallest_difference;
		t_combat_creature const*			closest = 0;

		angle = get_angle( direction );
		for (; index != end; index++)
		{
			if (is_friendly( **index ))
				continue;
			if (index->get()->get_number() == 0)
				continue;
			distance = get_edge_distance( **index, cell_point );
			if (closest != 0 && distance > closest_distance)
				continue;
			bearing = get_angle( index->get()->get_footprint_center()
				                 - get_footprint_center( cell_point ) );
			difference = get_difference( angle, bearing );
			// if two creatures are equally close, choose the one that
			// is closer to our front
			if (closest != 0 && distance == closest_distance 
				&& difference > smallest_difference)
				continue;
			closest = index->get();
			closest_distance = distance;
			smallest_difference = difference;
		}
		if (closest == 0)
		{ // if there are no enemies, turn to face screen
			if (direction > k_direction_south)
				direction = counter_clockwise( direction );
			else
				direction = clockwise( direction );
		}
		else
		{
			// turn to face the closest enemy
			bearing = get_angle( closest->get_footprint_center()
				                 - get_footprint_center( cell_point ) );
			bearing = (bearing - angle + 720) % 360;
			if (bearing > 180)
				direction = counter_clockwise( direction );
			else
				direction = clockwise( direction );
		}
	}

	return direction;
}


// -----------------------------------------------------------------------
// set creature to Wait
// -----------------------------------------------------------------------
void t_combat_creature::set_wait_animation()
{
	if (get_number() == 0)
		return;
	set_animation( k_combat_actor_action_wait, get_wait_direction(m_current_direction), t_combat_creature_handler() );
}

// -----------------------------------------------------------------------
// return true: creature cannot go into forbidden, upper zone
// -----------------------------------------------------------------------
bool t_combat_creature::is_creature() const
{
	return true;
}

// -----------------------------------------------------------------------
// return native terrain
// -----------------------------------------------------------------------
bool t_combat_creature::is_native_terrain( t_terrain_type terrain ) const
{
	if (is_active( k_spell_terrain_walk ))
		return true;
	return m_creature_stack->is_native_terrain( terrain );
}

// -----------------------------------------------------------------------
// creatures can be attacked in melee
// -----------------------------------------------------------------------
bool t_combat_creature::can_be_attacked() const
{
	return m_number > 0;
}


// ------------------------------------------------------------
// determine angle and action
// ------------------------------------------------------------
static t_combat_actor_action_id get_action( t_combat_creature const& attacker, 
										    t_combat_creature const& defender,
										    t_direction&			 direction )
{
	t_combat_actor_action_id action;

	if (defender.get_new_damage() >= defender.get_total_hits())
	{
		direction = get_direction( defender, attacker );
		action = k_combat_actor_action_die;
	}
	else if (defender.is_blocking_attack())
	{
		direction = get_direction( defender, attacker );
		if (defender.has_action( k_combat_actor_action_block, direction ))
			action = k_combat_actor_action_block;
		else
			action = k_combat_actor_action_flinch;
	}
	else if (defender.get_new_damage() == 0 && defender.robe_blocked_damage())
	{
		action = k_combat_actor_action_wait;
	}
	else
	{
		direction = defender.get_current_direction();
		action = k_combat_actor_action_flinch;
	}
	return action;
}

// ------------------------------------------------------------
// play an appropriate flinch animation for a list of creatures
// ------------------------------------------------------------
void t_combat_creature::preload_flinch( t_combat_creature const& attacker )
{
	t_combat_actor_action_id         action;
	t_direction                      direction;

	action = get_action( attacker, *this, direction );
	if (action != k_combat_actor_action_wait)
		m_battlefield.preload( *this, action, direction );
}

// ------------------------------------------------------------
// play an appropriate flinch animation for a list of creatures
// ------------------------------------------------------------
void t_combat_creature::flinch( t_combat_creature const& attacker, t_combat_action_message const & message )
{
	t_combat_actor_action_id         action;
	t_direction                      direction;

	action = get_action( attacker, *this, direction );
	if (action != k_combat_actor_action_wait)
	{
		if (action == k_combat_actor_action_block)
			m_battlefield.start_animation( *this, k_spell_effect_block, message );
		set_animation( action, direction, message );
	}
	check_guardian_robe();
}

// ------------------------------------------------------------
// return whether creature is still active
// ------------------------------------------------------------
bool t_combat_creature::is_alive() const
{
	return m_number > 0;
}

t_stationary_combat_object* t_combat_creature::get_tower() const
{
	return m_battlefield.get_tower( get_cell_position(), get_footprint() );
}


// ------------------------------------------------------------
// return if creature is 4th level
// ------------------------------------------------------------
bool t_combat_creature::is_giant() const
{
	t_creature_type creature = get_creature_type();

	return (creature != k_creature_none && get_traits( creature ).level >= 4);
}

int t_combat_creature::get_normal_alpha() const
{
	return m_normal_alpha;
}

// --------------------------------------------------------------------------------
// determine whether two creatures interfere
// --------------------------------------------------------------------------------
bool t_combat_creature::interferes( t_map_point_2d const&		center, int	facing, 
								    t_combat_creature const&	target, 
								    t_map_point_2d const&		target_cell ) const
{
	if (!is_awake())
		return false;

	// creatures in arrow towers can always shoot.
	if (target.is_in_tower())
		return false;

	if (target.get_controller() == get_controller() && !is_active( k_spell_berserk ))
		return false;
	
	if (!target.is_alive())
		return false;
		
	if (!target.is_ranged())
		return false;

	t_map_point_2d	delta;
	int				distance;
	int				angle;
	int				attack_range;
	t_map_point_2d  target_center = target.get_footprint_center( target_cell );

	delta = target_center - center;
	distance = sqrt( (double)(delta.row * delta.row + delta.column * delta.column) ) + 0.5;
	attack_range = (get_attack_range() + 1) << k_battlefield_subcell_shift;
	if (distance > attack_range + target.get_half_footprint_size() + get_half_footprint_size())
		return false;

	// check angle
	angle = get_angle( center, target_center );
	if ((get_difference( angle, facing ) << 1) <= get_arc_threat())
		return true;

	return false;
}

bool t_combat_creature::interferes( t_combat_creature const& target ) const
{
	return interferes( get_footprint_center(), get_angle( get_current_direction() ), target,
					   target.get_cell_position() );
}

			    
// --------------------------------------------------------------------------------
// generate a inteference list, given a position and an angle
// all those creatures we intefere with
// --------------------------------------------------------------------------------
void t_combat_creature::get_interfere_with_list( t_combat_creature_list&	list,
												 t_map_point_2d const&		position,
												 t_direction				direction ) const
{
	list.clear();

	t_battlefield&							battlefield = m_battlefield;
	t_combat_creature_list::const_iterator	index;
	t_combat_creature_list::const_iterator	end = battlefield.creatures_end();
	int										facing;
	t_map_point_2d							footprint_center;
	
	if (!is_awake())
		return;

	facing = get_angle( direction );
	footprint_center = get_footprint_center( position );
	for (index = battlefield.creatures_begin(); index != end; ++index )
	{
		if (interferes( footprint_center, facing, **index, (*index)->get_cell_position() ))
			// Add to list
			list.push_back( *index );
	}
}

// --------------------------------------------------------------------------------
// get a list of creatures which would interfere with this creature
// at a given position
// --------------------------------------------------------------------------------
void t_combat_creature::get_interfered_by_list( t_combat_creature_list&  list,
											   t_map_point_2d const&	cell_position ) const
{
	list.clear();

	// creatures in arrow towers can always shoot.
	if (is_in_tower())
		return; 

	if (!is_alive())
		return;

	if (!is_ranged())
		return;

	t_battlefield&							battlefield = m_battlefield;
	t_combat_creature_list::const_iterator	index;
	t_combat_creature_list::const_iterator	end = battlefield.creatures_end();
	
	for (index = battlefield.creatures_begin(); index != end; index++)
	{
		t_combat_creature const& target = **index;

		if (target.interferes( target.get_footprint_center(), 
							   get_angle( target.get_current_direction() ),
							   *this, cell_position ))
		{
			// Add to list
			list.push_back( *index );
		}
	}
}

// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
t_map_point_3d t_combat_creature::get_position_in_tower( t_stationary_combat_object* tower ) const
{
	t_battlefield&				battlefield = m_battlefield;
	t_map_point_3d				position;
	int							footprint_size;

	assert( tower != 0 );
	assert( tower->get_obstacle_type() == k_obstacle_castle_tower );
	position = tower->get_position();
	footprint_size = tower->get_half_footprint_size() + 1;
	position.row += footprint_size;
	position.column += footprint_size;
	footprint_size = get_half_footprint_size();
	position.row -= footprint_size;
	position.column -= footprint_size;
	position.height += tower->get_height();
	return position;
}


// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
void t_combat_creature::place_in_tower( t_stationary_combat_object* tower )
{
	t_battlefield&				battlefield = m_battlefield;
	t_map_point_3d				position;
	t_map_point_2d				point;

	assert( tower != 0 );
	assert( tower->get_obstacle_type() == k_obstacle_castle_tower );
	assert( !m_in_tower );
	position = get_position_in_tower( tower );
	m_in_tower = true;
	battlefield.move_object( *this, position );
	point = get_cell_position();
	// setup special cases for creatures in towers
	set_shadow_displacement( 0, battlefield );
	battlefield.unstamp( this, get_footprint(), point );
	battlefield.stamp( this, tower->get_footprint(), tower->get_cell_position() );
}


// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
void t_combat_creature::place_in_tower( t_map_point_2d const& point )
{
	t_battlefield&				battlefield = m_battlefield;
	t_stationary_combat_object* tower;
	t_battlefield_cell const&   cell = battlefield.get_cell( point );

	tower = cell.get_obstacle();
	place_in_tower( tower );
}

// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
void t_combat_creature::place_in_tower()
{
	t_map_point_2d				point = get_cell_position();
	t_stationary_combat_object* tower;

	tower = m_battlefield.get_tower( point, get_footprint() );
	place_in_tower( tower );
}

// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
void t_combat_creature::remove_from_tower()
{
	t_battlefield&				battlefield = m_battlefield;
	t_stationary_combat_object* tower;
	t_map_point_2d				point = get_cell_position();
	int							footprint_size;

	footprint_size = get_footprint_size();
	point.row += footprint_size >> 1;
	point.column += footprint_size >> 1;
	tower = battlefield.get_cell( point ).get_obstacle();
	assert( tower != 0 );
	assert( tower->get_obstacle_type() == k_obstacle_castle_tower );
	assert( m_in_tower );
	battlefield.unstamp( this, tower->get_footprint(), tower->get_cell_position() );
	battlefield.stamp( this, get_footprint(), get_cell_position() );
	m_in_tower = false;
}

// ------------------------------------------------------------
// place a creature in a tower
// ------------------------------------------------------------
bool t_combat_creature::is_elevated() const
{
	return m_in_tower;
}

// ------------------------------------------------------------
// get adjusted creature drawing order
// ------------------------------------------------------------
int	t_combat_creature::get_depth() const
{
	int depth			= t_combat_actor::get_depth();
	int footprint_size	= get_footprint_size();

	if (footprint_size > 4)
	{
		int footprint_subcell_size = footprint_size
		                             << k_battlefield_subcell_shift;
		depth -= footprint_subcell_size - (4 << k_battlefield_subcell_shift);
	}
	if (m_in_tower && is_underlay())
		depth -= k_depth_underlay;  // undo underlay drawing order adjustment if in tower
	return depth;
}

// ------------------------------------------------------------
// get adjusted creature drawing order
// ------------------------------------------------------------
t_attackable_object* t_combat_creature::get_attackable_object()
{
	return this;
}

// -----------------------------------------------------------------------
// virtual function from attackable object
// -----------------------------------------------------------------------
t_abstract_combat_object const* t_combat_creature::get_const_object() const
{
	return this;
}

t_combat_object_list t_combat_creature::get_objects() const
{
	t_combat_object_list result;

	result.push_back( const_cast<t_combat_creature*>(this) );
	return result;
}

// ------------------------------------------------------------
// get chance creature will be stoned
// ------------------------------------------------------------
int t_combat_creature::get_stoning_chance( t_combat_creature const& target ) const
{
	int result;

	if (!can_affect( *this, k_spell_stone_gaze, target ))
		return 0;

	result = get_spell_chance( target, k_spell_stone_gaze );
	result = target.m_creature_stack->get_stoning_chance() * result / 100;
	return result;
}

// ------------------------------------------------------------
// set retaliation marker
// ------------------------------------------------------------
void t_combat_creature::set_retaliated( bool arg )
{
	if (m_has_retaliated == arg)
		return;
	m_has_retaliated = arg;
	if (arg)
		m_retaliation_time_stamp = m_battlefield.get_retaliation_time_stamp()
		                           - k_retaliation_turn_increment;
}

// ------------------------------------------------------------
// check if retaliation should be reset
// ------------------------------------------------------------
void t_combat_creature::update_retaliation( int time_stamp )
{
	if (!m_has_retaliated)
		return;
	if (time_stamp > m_retaliation_time_stamp)
		return;
	m_has_retaliated = false;
}

// ------------------------------------------------------------
// calculate where an animation should appear
// ------------------------------------------------------------
t_screen_point t_combat_creature::get_animation_position( t_animation const* animation ) const
{
	t_screen_rect	animation_rect = animation->get_rect();
	t_screen_point  animation_size = animation_rect.size();
	t_screen_point  base_point = animation->get_base_point();
	t_screen_point  origin;
	t_map_point_3d  position = get_footprint_center();

	position.height += get_height();
	origin = m_battlefield.subcell_to_screen_point( position );
	if (animation->x_is_valid())
		origin.x -= base_point.x;
	else
		origin.x -= animation_size.x >> 1;
	if (animation->y_is_valid())
		origin.y -= base_point.y;
	else
		origin.y -= animation_size.y;
	if (origin.y < 0 && animation_rect.height() < 350)
		origin.y = 0;
	return origin - animation_rect.top_left();
}

// ------------------------------------------------------------
// Hitpoints remaining before 1 creature dies
// ------------------------------------------------------------
int t_combat_creature::get_remaining_hitpoints( ) const
{
	int hit_points = get_hit_points();
	
    if ( is_active(k_spell_celestial_armor) )
		hit_points += get_effect_power( k_spell_celestial_armor );
	if ( is_active(k_spell_life_drain) )
		hit_points += get_effect_power( k_spell_life_drain );

	return hit_points - get_wounds();
}

// ------------------------------------------------------------
// update any continous animation over the head of the creature
// ------------------------------------------------------------
void t_combat_creature::change_active_spell_effect()
{
	if (m_active_spell_animation_window != 0)
	{
		m_active_spell_animation_window->close();
		m_active_spell_animation_window = 0;
	}
	m_active_spell_animation = t_animation_ptr();
	if (m_spell_animation != 0)
		return;

	if (!is_alive())
		return;

	t_battlefield&	battlefield = m_battlefield;
	double			scale = battlefield.get_model_scale();
	t_window*       window = battlefield.get_window();

	if (window == 0)
		return; // only true when battlefield is closing.

	if (is_active( k_spell_berserk ))
		m_active_spell_animation = ::get_spell_animation( k_spell_effect_berserk, scale );
	else if (is_active( k_spell_hypnotize ) || is_active( k_spell_short_hypnotism ))
		m_active_spell_animation = ::get_spell_animation( k_spell_effect_hypnotize, scale );
	else
		return;

	t_screen_point     origin;

	origin = get_animation_position( m_active_spell_animation );
	m_active_spell_animation_window = new t_animated_window( m_active_spell_animation, 
		                                                     origin, 100,
															 battlefield.get_window() );
}

// ------------------------------------------------------------
// get color for messages
// ------------------------------------------------------------
t_pixel_24 const& t_combat_creature::get_message_color() const
{
	static t_pixel_24 const colors[] =
	{
		t_pixel_24( 255, 0, 0 ),	// red
		t_pixel_24( 32, 62, 255 ),	// blue
		t_pixel_24( 100, 255, 48 ),	// green
		t_pixel_24( 255, 160, 33 ), // orange
		t_pixel_24( 255, 39, 255 ), // purple
		t_pixel_24( 3, 217, 255 ),  // teal
		t_pixel_24( 217, 217, 217 ), // gray
	};

	t_player_color player_color;

	player_color = m_battlefield.get_player_color( belongs_to_defender() );

	return colors[player_color];
}

double get_total_weight( t_creature_int_map const& damage_map, bool side )
{
	t_creature_int_map::const_iterator	index = damage_map.begin();
	t_creature_int_map::const_iterator	end	= damage_map.end();
	double								result = 0.0;
	t_combat_creature const*			creature;

	for (; index != end; ++index)
	{
		creature = index->first;
		if (creature->belongs_to( side ))
			result -= creature->get_ai_value( index->second );
		else
			result += creature->get_ai_value( index->second );
	}
	return result;
}

// ---------------------------------------------------------------
// compute effects of a melee attack
// ---------------------------------------------------------------
void t_combat_creature::get_melee_exchange_effect( t_combat_creature const&	target,
												   int						distance_moved,
												   t_creature_int_map&		damage_map,
												   t_attack_angle const&	attack_angle,
												   t_map_point_2d const&	target_position,
												   bool						retaliate,
												   bool						attacker_has_first_strike,
												   bool						defender_has_first_strike ) const
{
	t_battlefield &battlefield = m_battlefield;

	// Determine retaliation effects 
	int attacks = get_attacks( false );
	assert( target_position != attack_angle.point );
	
	if (retaliate)
	{
		// Retaliation possible...
		t_attack_angle retaliation_angle = attack_angle;

		retaliation_angle.point = target_position;
		retaliation_angle.attack_angle = (attack_angle.attack_angle + 180) % 360;

		--attacks;
		if (attacker_has_first_strike == defender_has_first_strike)
		{
			t_creature_int_map retaliation_damage;

			get_melee_effect( target, distance_moved, damage_map, attack_angle, target_position );
			target.get_melee_effect( *this, 0, retaliation_damage, retaliation_angle, 
									 attack_angle.point );

			t_creature_int_map::iterator index = retaliation_damage.begin();
			t_creature_int_map::iterator end = retaliation_damage.end();

			for (; index != end; ++index)
				damage_map[index->first] += index->second;

		}
		else if (attacker_has_first_strike && !defender_has_first_strike)
		{
			get_melee_effect( target, distance_moved, damage_map, attack_angle, target_position );
			target.get_melee_effect( *this, 0, damage_map, retaliation_angle, 
									 attack_angle.point );
		}
		else
		{
			target.get_melee_effect( *this, 0, damage_map, retaliation_angle, 
									 attack_angle.point );
			get_melee_effect( target, distance_moved, damage_map, attack_angle, target_position );
		}
	}

	while (attacks--)
		get_melee_effect( target, distance_moved, damage_map, attack_angle, target_position );
}

// ---------------------------------------------------------------
// compute effects of a melee attack
// ---------------------------------------------------------------
void t_combat_creature::get_melee_exchange_effect( t_combat_creature const&	target,
												   int						distance_moved,
												   t_creature_int_map&		damage_map,
												   t_attack_angle const&	attack_angle,
												   t_map_point_2d const&	target_position,
												   bool						retaliate ) const
{
	t_battlefield &battlefield = m_battlefield;

	bool attacker_has_first_strike = false;
	bool defender_has_first_strike = false;
	
	if (retaliate)
	{
		// Retaliation possible...
		attacker_has_first_strike = has_ability( k_ability_first_strike )
								 && !target.has_ability( k_ability_first_strike_immunity );
		defender_has_first_strike = target.has_ability( k_ability_first_strike )
								 && !has_ability( k_ability_first_strike_immunity );

		if (!target.is_awake())
		{
			attacker_has_first_strike = true;
			defender_has_first_strike = false;
		}
		if (!attacker_has_first_strike && distance_moved < 4)
			defender_has_first_strike = false;
	}
	get_melee_exchange_effect( target, distance_moved, damage_map, attack_angle, target_position,
							   retaliate, attacker_has_first_strike, defender_has_first_strike );
}

// ---------------------------------------------------------------
// compute effects of a ranged attack
// ---------------------------------------------------------------
int t_combat_creature::project_ranged_damage( t_combat_creature const&	target, 
										      int&						retaliation,
											  t_combat_creature *&	retaliation_target ) const
{
	int damage = 0;
	int attacks = get_attacks( true );
	
	retaliation = 0;
	retaliation_target = (t_combat_creature *)this;
	if ( target.can_retaliate( *this, true ) 
		 && m_battlefield.get_difficulty() >= k_difficulty_normal )
	{
		// Retaliation possible...
		bool attacker_has_first_strike = has_ability( k_ability_ranged_first_strike );
		bool defender_has_first_strike = target.has_ability( k_ability_ranged_first_strike );
		t_battlefield&	battlefield = m_battlefield;

		if (!target.is_awake())
		{
			attacker_has_first_strike = true;
			defender_has_first_strike = false;
		}

		// What will the retaliation actually hit?
		retaliation_target = battlefield.get_ranged_target( target, *this );
		assert( retaliation_target != 0 );
		--attacks;

		damage = get_ranged_damage( target, 0, 0 );
		if (attacker_has_first_strike && !defender_has_first_strike)
		{
			// Do attacker damage first
			retaliation = target.get_ranged_damage( *retaliation_target, damage, 0 );
		}
		else
		{
			retaliation = target.get_ranged_damage( *retaliation_target, 0, 0 );
		}
	}
	while (attacks--)
	{
		if (retaliation_target == this)
			damage += get_ranged_damage( target, retaliation, damage );
		else
			damage += get_ranged_damage( target, 0, damage );
	}
	return damage;
}

// -------------------------------------------------------------------------------
// return a path finder
// -------------------------------------------------------------------------------
t_combat_path_finder& t_combat_creature::get_path_finder( bool ignore_friendly ) const
{
	t_owned_ptr<t_combat_path_finder>& ptr = m_ai_data->path_finder[ignore_friendly];

	if (ptr.get() == 0)
	{
		ptr.reset( new t_combat_path_finder( *this, ignore_friendly ) );
	}
	return *ptr;
}

// -------------------------------------------------------------------------------
// compute maximum amount of base damage that is useful for this creature
// -------------------------------------------------------------------------------
int	t_combat_creature::get_damage_done_limit( bool ranged ) const
{
	if (m_ai_data->damage_done_limit[ranged] < 0)
	{
		t_battlefield&	battlefield = m_battlefield;

		if (ranged && !battlefield.can_shoot( *this ))
		{
			m_ai_data->damage_done_limit[ranged] = 0;
			return 0;
		}

		t_combat_creature_list::iterator	index = battlefield.creatures_begin();
		t_combat_creature_list::iterator	end = battlefield.creatures_end();
		t_combat_creature*					creature;
		int									damage;
		bool								first = true;
		int									total_hits;

		for (; index != end; ++index)
		{
			creature = *index;
			if (creature->belongs_to_defender() == get_controller())
				continue;
			if (creature->get_number() == 0)
				continue;
			if (creature->is_active( k_spell_sanctuary ))
				continue;
			total_hits = creature->get_total_hits();
			damage = adjust_attack_damage( 100, *creature, ranged, get_combat_movement(), 
										   k_wall_bonus_none );
			damage = total_hits * 100 / damage;
			if (first || m_ai_data->damage_done_limit[ranged] < damage)
			{
				m_ai_data->damage_done_limit[ranged] = damage;
			}
			first = false;
		}
		if (first)
		{
			m_ai_data->damage_done_limit[ranged] = 0;
		}
	}
	return m_ai_data->damage_done_limit[ranged];
}

// -------------------------------------------------------------------------------
// compute the most damage we'll take from enemy creatures
// -------------------------------------------------------------------------------
int t_combat_creature::get_damage_taken_limit( bool ranged ) const
{
	if (m_ai_data->damage_taken_limit[ranged] < 0)
	{
		t_battlefield&						battlefield = m_battlefield;
		t_combat_creature_list::iterator	index = battlefield.creatures_begin();
		t_combat_creature_list::iterator	end = battlefield.creatures_end();
		t_combat_creature*					creature;

		m_ai_data->damage_taken_limit[ranged] = 0;
		for (; index != end; ++index)
		{
			creature = *index;
			if (creature->belongs_to_defender() == get_controller())
				continue;
			if (creature->get_number() == 0)
				continue;
			if (creature->is_incapacitated())
				continue;
			if (creature->is_active( k_spell_song_of_peace ))
				continue;
			if (ranged && !battlefield.can_shoot( *creature ))
				continue;

			int damage_low  = creature->get_damage_low();
			int damage_high = creature->get_damage_high();
			int damage		= (damage_low + damage_high) * creature->get_number()
							* (creature->get_attacks( ranged ) + 1) / 4;

			damage = creature->adjust_attack_damage( damage, *this, ranged,
													 creature->get_combat_movement(),
													 k_wall_bonus_none );
			if (m_ai_data->damage_taken_limit[ranged] < damage)
			{
				m_ai_data->damage_taken_limit[ranged] = damage;
			}
		}
	}
	return m_ai_data->damage_taken_limit[ranged];
}

// -------------------------------------------------------------------------------
// compute the relative danger of different types of damage
// -------------------------------------------------------------------------------
double t_combat_creature::get_threat_type_weight( bool ranged ) const
{
	if (m_ai_data->threat_type_weight[ranged] < 0.0)
	{
		t_battlefield&						battlefield = m_battlefield;
		t_combat_creature_list::iterator	index = battlefield.creatures_begin();
		t_combat_creature_list::iterator	end = battlefield.creatures_end();
		t_combat_creature*					creature;
		int									i;
		int									j;
		int									adjusted_damage[2];

		for (i = 0; i < 2; ++i)
			m_ai_data->threat_type_weight[i] = 0.0;
		for (; index != end; ++index)
		{
			creature = *index;
			if (creature->belongs_to_defender() == get_controller())
				continue;
			if (creature->get_number() == 0)
				continue;
			if (creature->is_incapacitated())
				continue;
			if (creature->is_active( k_spell_song_of_peace ))
				continue;

			int damage_low  = creature->get_damage_low();
			int damage_high = creature->get_damage_high();
			int damage		= (damage_low + damage_high) * creature->get_number() / 2;
			
			for (j = 0; j < 2; ++j)
			{
				if (j == 1 && !battlefield.can_shoot( *creature ))
					adjusted_damage[j] = 0;
				else
				{
					adjusted_damage[j]  = damage * (creature->get_attacks( j != 0 ) + 1) / 2;

					adjusted_damage[j] = creature->adjust_attack_damage( adjusted_damage[j], *this, j != 0,
																creature->get_combat_movement(),
																k_wall_bonus_none );
				}
			}
			if (adjusted_damage[0] > adjusted_damage[1])
				m_ai_data->threat_type_weight[0] += adjusted_damage[0];
			else
				m_ai_data->threat_type_weight[1] += adjusted_damage[1];
		}
		
		double total = m_ai_data->threat_type_weight[0] + m_ai_data->threat_type_weight[1];

		if (total != 0.0)
		{
			for (i = 0; i < 2; ++i)
				m_ai_data->threat_type_weight[i] /= total;
		}
	}
	return m_ai_data->threat_type_weight[ranged];
}

// -------------------------------------------------------------------------------
// get value of decreasing the creature's damage
// -------------------------------------------------------------------------------
double t_combat_creature::get_damage_decrease_ai_value( double decrease, bool ranged ) const
{
	if (is_incapacitated())
		return 0.0;
	if (is_active( k_spell_song_of_peace ))
		return 0.0;
	if (decrease <= 0.0)
		return 0.0;

	double  damage;
	double	new_value;
	double	old_value;
	double  multiplier;

	// compute the total amount of new damage we're doing per turn.
	damage = (get_damage_low() + get_damage_high()) * 0.5;
	multiplier = get_basic_damage_multiplier( *this, ranged );
	if (ranged)
		multiplier *= get_ai_ranged_reduction();
	else
		multiplier *= get_ai_melee_reduction();
	old_value = damage * multiplier;
	new_value = (damage - decrease) * multiplier;

	// compute value of attack
	double minimum_value = get_ai_spellcasting_value_per_hit();
	double tactics_value = get_ai_tactics_value_per_hit();

	if (minimum_value < tactics_value)
		minimum_value = tactics_value;
	minimum_value *= get_total_hits();

	if (old_value < minimum_value)
		old_value = minimum_value;
	if (new_value < minimum_value)
		new_value = minimum_value;
	return old_value - new_value;
}

// -------------------------------------------------------------------------------
// get value of decreasing the creature's damage
// -------------------------------------------------------------------------------
double t_combat_creature::get_damage_decrease_ai_value( double decrease ) const
{
	double result = get_damage_decrease_ai_value( decrease, false );

	if (m_battlefield.can_shoot( *this ))
	{
		double ranged_result = get_damage_decrease_ai_value( decrease, true );

		if (result < ranged_result)
			result = ranged_result;
	}
	return result;
}

// -------------------------------------------------------------------------------
// get value of increasing the creature's damage
// -------------------------------------------------------------------------------
double t_combat_creature::get_damage_increase_ai_value( double increase, bool ranged ) const
{
	if (is_incapacitated())
		return 0.0;
	if (is_active( k_spell_song_of_peace ))
		return 0.0;
	if (increase <= 0.0)
		return 0.0;

	double	new_damage;
	double	value;
	double	limit;

	limit = get_damage_done_limit( ranged );
	if (limit <= 0.0)
		return 0.0;
	// compute the total amount of new damage we're doing per turn.
	new_damage = (get_damage_low() + get_damage_high()) * 0.5 + increase;
	new_damage = new_damage * get_number() * (get_attacks( ranged ) + 1) * 0.5;
	if (new_damage > limit)
		new_damage = limit;
	value = new_damage * get_offense( ranged );
	if (ranged)
		value *= get_ai_ranged_reduction();
	else
		value *= get_ai_melee_reduction();

	int		hit_points = get_hit_points();
	int		total_hits = get_total_hits();
	double	old_value = get_ai_value_per_hit() * total_hits;

	// adjust new value by the number of actual hit points remaining (actual total hits vs.
	// theoretical hits)
	value = value * total_hits / (get_number() * hit_points);
	if (value < old_value)
		return 0.0;
	return value - old_value;
}

// -------------------------------------------------------------------------------
// get value of increasing the creature's damage
// -------------------------------------------------------------------------------
double t_combat_creature::get_damage_increase_ai_value( double increase ) const
{
	double result = get_damage_increase_ai_value( increase, false );

	if (m_battlefield.can_shoot( *this ))
	{
		double ranged_result = get_damage_increase_ai_value( increase, true );

		if (result < ranged_result)
			result = ranged_result;
	}
	return result;
}

// -------------------------------------------------------------------------------
// get value of increasing the creature's defense
// -------------------------------------------------------------------------------
double t_combat_creature::get_defense_increase_ai_value( double increase, bool ranged ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses;

	// compute how much of this we expect to lose
	losses = data.get_approximate_losses( *this );
	if (losses <= 0)
		return 0.0;

	if (losses >= get_number())
	{
		// we assume we're going to lose this stack.
		// we need to check if there's overkill, i.e. the worst-case attack
		// will more than destroy this stack, and the extra defense may 
		// not help.
		// compute how much damage we'll guess we'll take with the new defense
		double max_damage = get_damage_taken_limit( ranged );
		double new_damage = max_damage / (increase + 1.0);
		double total_hits = get_total_hits();

		// limit both damages to the actual hit points
		if (max_damage > total_hits)
			max_damage = total_hits;
		if (new_damage > total_hits)
			new_damage = total_hits;

		if (new_damage >= max_damage)
			return 0.0;
		// compute the actual value of the added defense
		increase = max_damage / new_damage - 1.0;
	}
	// compute how much value we think we'll save with the increased defense.
	double weight = get_ai_value_per_hit() * losses * get_hit_points() * increase;

	weight *= get_threat_type_weight( ranged );
	return weight;
}

// -------------------------------------------------------------------------------
// get value of increasing the creature's defense
// -------------------------------------------------------------------------------
double t_combat_creature::get_defense_decrease_ai_value( double decrease, bool ranged ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses;

	// compute how much of this we expect to lose
	losses = data.get_approximate_losses( *this );
	if (losses <= 0)
		return 0.0;

	if (losses >= get_number())
	{
		// we assume we're going to lose this stack.
		// we need to check if there's overkill, i.e. the attack
		// will more than destroy this stack, and the extra damage may 
		// not help.
		// compute how much damage we'll guess we'll do with the new, lower defense
		double max_damage = get_damage_taken_limit( ranged );
		double new_damage = max_damage * (decrease + 1.0);
		double total_hits = get_total_hits();

		// limit both damages to the actual hit points
		if (max_damage > total_hits)
			max_damage = total_hits;
		if (new_damage > total_hits)
			new_damage = total_hits;

		if (new_damage <= max_damage)
			return 0.0;
		// compute the actual value of the added damage from reduce defense
		decrease = new_damage / max_damage - 1.0;
	}
	// compute how much extra value we'll destroy with the reduced defense
	double weight = get_ai_value_per_hit() * losses * get_hit_points() * decrease;

	weight *= get_threat_type_weight( ranged );
	return weight;
}

// ---------------------------------------------------------------
// approximate value of using up one action for this creature.  Primarily used for 
// blind and similar spells.
// ---------------------------------------------------------------
double t_combat_creature::get_ai_value_per_action() const
{
	// note: does not use get_ai_value( damage ), because we don't want the
	// bonus for kills that function uses.
	// big assumption: creature enemy value at 1/5th of its current value
	return get_ai_value_per_hit() * get_total_hits() * 0.2;
}

// ---------------------------------------------------------------
// determine if creature can cast any spell on a list
// ---------------------------------------------------------------
static bool can_cast_spells( t_combat_creature const& creature, t_spell const* spells,
							 int spell_count )
{
	int i;

	for (i = 0; i < spell_count; ++i)
		if (creature.can_cast( spells[i] ))
			return true;
	return false;
}

// ---------------------------------------------------------------
// determine if this creature has any non-missile damage spells
// ---------------------------------------------------------------
bool t_combat_creature::has_nonmissile_damage_spells() const
{
	static t_spell const k_spells[] = 
	{ 
		k_spell_chain_lightning,
		k_spell_disintegrate,
		k_spell_fire_ring,
		k_spell_fireball,
		k_spell_implosion,
		k_spell_inferno,
		k_spell_kreegan_fire,
		k_spell_lightning,
	};
	return ::can_cast_spells( *this, k_spells, ELEMENTS_OF( k_spells ));
}

bool t_combat_creature::has_damage_spells() const
{
	static t_spell const k_spells[] = 
	{ 
		k_spell_burning_hands,
		k_spell_chain_lightning,
		k_spell_disintegrate,
		k_spell_fire_bolt,
		k_spell_fire_ring,
		k_spell_fireball,
		k_spell_ice_bolt,
		k_spell_implosion,
		k_spell_inferno,
		k_spell_kreegan_fire,
		k_spell_lightning,
		k_spell_magic_arrow,
		k_spell_magic_fist
	};
	return ::can_cast_spells( *this, k_spells, ELEMENTS_OF( k_spells ));
}

bool t_combat_creature::has_missile_spells() const
{
	static t_spell const k_spells[] = 
	{
		k_spell_fire_bolt, k_spell_ice_bolt, k_spell_magic_arrow,
		k_spell_magic_fist
	};
	return ::can_cast_spells( *this, k_spells, ELEMENTS_OF( k_spells ));
}

// ------------------------------------------------------------------------------
// get value of a change in movement
// ------------------------------------------------------------------------------
double t_combat_creature::get_ai_move_value( int change ) const
{
	t_combat_creature* target = get_closest_melee_target();

	if (target == 0)
		return 0.0;

	t_battlefield&				battlefield = m_battlefield;
	t_ai_combat_data_cache&		data		= battlefield.get_data_cache();
	t_attack_angle_list const&	attack_angles = data.get_attack_angles( *this, *target );
	t_attack_angle const*		attack_angle = attack_angles.get_closest();
	int							distance = attack_angle->move_distance;
	int							movement = get_combat_movement();
	int							old_time;
	int							new_time;

	if (distance == 0)
		return 0.0;

	old_time = (distance + movement - 1) / movement;
	movement += change;
	if (movement < 100)
		movement = 100;
	new_time = (distance + movement - 1) / movement;

	if (old_time == new_time)
		return 0.0;

	double melee_value;
	double old_value;
	double new_value;
	double current_value;

	melee_value = (get_damage_low() + get_damage_high()) * 0.5
			    * get_basic_damage_multiplier( *this, false );
	old_value = melee_value / old_time;
	new_value = melee_value / new_time;
	current_value = get_ai_value_per_hit() * get_number() * get_hit_points();
	
	// if this is an increase in speed (and an increase in value)...
	if (old_value < new_value)
	{
		// report the difference, but compare the new value against the greater
		// of the old melee value or the current overall value.
		if (old_value < current_value)
			old_value = current_value;
		if (old_value >= new_value)
			return 0.0;
		return new_value - old_value;
	}
	// if this is a decrease in speed ( and a decrease in value )...
	// if the old value is less than the current value, assume that speed doesn't
	// matter.
	if (old_value < current_value)
		return 0.0;
	return new_value - old_value;
}

// ------------------------------------------------------------------------------
// get value of a change in speed
// ------------------------------------------------------------------------------
double t_combat_creature::get_ai_speed_value( int speed_change, int morale_change ) const
{
	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;
	double								value;
	int									speed;
	int									new_speed;
	int									enemy_speed;
	double								total_enemies = 0.0;
	double								changed_enemies = 0.0;

	speed = get_speed() + get_morale() * 100;
	new_speed = speed + speed_change + morale_change * 100;
	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() != get_controller())
			continue;
		if (creature->is_incapacitated())
			continue;
		value = creature->get_ai_value_per_hit() * creature->get_total_hits();
		total_enemies += value;
		enemy_speed = creature->get_speed() + creature->get_morale() * 100;
		if (new_speed < speed)
		{
			if (enemy_speed <= speed && enemy_speed > new_speed)
				changed_enemies -= value;
		}
		else
		{
			if (enemy_speed >= speed && enemy_speed < new_speed)
				changed_enemies += value;
		}
	}
	if (total_enemies == 0.0)
		return 0.0;
	return get_ai_value_per_action() * 0.5 * changed_enemies / total_enemies;
}

void t_combat_creature::add_creatures( int added_health )
{
	t_creature_ptr	new_creature;
	int				old_number = get_number();
	int				total_hits = get_total_hits() + added_health;
	int				hit_points = get_hit_points();
	int				new_number = (total_hits + hit_points - 1) / hit_points;
	int				added = new_number - old_number;

	if (added > 0)
	{
		t_creature*		old_creature_stack;

		assert( is_summoned() );
		new_creature = new t_creature( get_creature_type(), added );
		old_creature_stack = dynamic_cast<t_creature*>( get_creature_stack() );
		old_creature_stack->add( *new_creature );
	}
}

// -----------------------------------------------------------------------
// Called from t_battlefield::clear(); absolutely the last chance to clean up
// -----------------------------------------------------------------------
void t_combat_creature::on_battlefield_destruction()
{
	clear_influence();
	set_animation( 0 );
	set_melee_action( t_ai_melee_action_ptr() );
	set_spell_animation( 0 );
	set_magic_leech_client( 0 );
	set_martyr_protector( 0 );
	m_new_poison_source = NULL;

	t_combat_actor::on_battlefield_destruction();
}


// ------------------------------------------------------------------------------
// read / write
// ------------------------------------------------------------------------------
t_combat_object_type t_combat_creature::get_object_type() const
{
	return k_combat_object_type_creature;
}

static bool read_abilities( std::streambuf& stream, bool* abilities )
{	
	int					count;
	t_creature_ability	ability;

	memset( abilities, 0, sizeof( bool ) * k_ability_count );
	count = get<t_uint16>( stream );
	while (count--)
	{
		ability = t_creature_ability( get<t_int16>( stream ));
		abilities[ability] = true;
	}
	return true;
}

static int const k_current_version = 1;

bool t_combat_creature::read( t_combat_reader& reader )
{
	std::streambuf& stream = reader.get_stream();
	t_battlefield&	battlefield = reader.get_battlefield();
	int				version = get<t_uint16>( stream );

	if (version < 0 || version > k_current_version)
		return false;
	if (!t_combat_actor::read( reader ))
		return false;
	if (!t_abstract_combat_creature::read( stream ))
		return false;

	int army_slot_number = get<t_int8>( stream );

	if (army_slot_number <  0)
	{	// create a creature stack
		t_creature_type creature_type = t_creature_type( get<t_int16>( stream ));
		int				number = get<t_uint32>( stream );

		m_creature_stack = new t_creature( creature_type, number );
	}
	else
	{
		bool				army_side = ( get<t_uint8>( stream ) != 0 );
		t_creature_array&	army = battlefield.get_army( army_side );

		m_creature_stack = &army[army_slot_number];
	}
	m_model_ptr = m_creature_stack->get_combat_model( battlefield.get_model_scale() );

	m_active_shadow = reader.read_abstract_object();
	m_ai_data.reset( new t_combat_creature_ai_data );
	::read( stream, m_ai_retaliation_weight_reduction, sizeof( m_ai_retaliation_weight_reduction ));
	m_artifact_move_bonus			= get<t_int16>( stream );
	m_artifact_spell_cost_modifier	= get<t_int16>( stream );
	::read( stream, m_base_defense, sizeof( m_base_defense ));
	assert( m_blocking_attack == false );
	m_block_chance	= get<t_int16>( stream );
	m_default_spell = t_spell( get<t_int16>( stream ));
	m_defending		= (get<t_uint8>( stream ) != 0);
	m_defense_added = get<t_int16>( stream );
	::read( stream, m_defense_bonus, sizeof( m_defense_bonus ));
	m_got_bad_luck		= (get<t_uint8>( stream ) != 0 );
	m_got_bad_morale	= (get<t_uint8>( stream ) != 0);
	m_got_good_luck		= (get<t_uint8>( stream ) != 0);
	m_got_good_morale	= (get<t_uint8>( stream ) != 0);
	m_has_leeched		= (get<t_uint8>( stream ) != 0);
	m_has_regenerated	= (get<t_uint8>( stream ) != 0);
	assert( m_in_water == false );
	assert( m_influence_set == false );
	m_guardian_angel_limit = 0;
	m_guardian_robe_strength = get<t_int16>( stream );

	read_abilities( stream, m_has_ability );
	m_is_underlay			= (get<t_uint8>( stream ) != 0);
	m_label					= reader.read_label();
	m_luck_bonus			= get<t_int16>( stream );
	m_magic_leech_client	= reader.read_creature();
	m_magic_resistance_bonus	= get<t_int16>( stream );
	
	int count = get<t_int16>( stream );

	while (count--)
	{
		t_combat_creature* client = reader.read_creature();

		m_martyr_clients.push_back( client );
	}
	m_martyr_protector	= reader.read_creature();
	m_morale			= get<t_int16>( stream );
	m_morale_bonus		= get<t_int16>( stream );
	m_morale_roll		= get<t_uint8>( stream );
	m_morale_shown		= (get<t_uint8>( stream ) != 0);
	m_move_bonus		= get<t_int16>( stream );
	m_new_damage_is_permanent	= false;
	m_new_poison				= 0;
	m_normal_alpha				= get<t_uint8>( stream );
	read_abilities( stream, m_normally_has_ability );
	m_offense_bonus	= get<t_int16>( stream );
	m_plague_checked	= (get<t_uint8>( stream ) != 0);
	m_poison_checked	= (get<t_uint8>( stream ) != 0);
	m_preferred_action	= t_combat_cursor_mode( get<t_int8>( stream ));
	m_retaliation_time_stamp	= get<t_int32>( stream );
	m_robe_blocked_damage		= false;
	m_selected					= (get<t_uint8>( stream ) != 0 );
	m_selection_shadow			= reader.read_abstract_object();
	assert( m_spell_animation == 0);
	m_tactics_speed_bonus		= get<t_int16>( stream );
	m_terrain_defense			= 0;
	m_waiting					= (get<t_uint8>( stream ) != 0);
	if (version >= 1)
		m_in_tower = get<bool>( stream );
	else
		m_in_tower = false;

	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
		if (m_spells[spell].active)
			m_active_spells.push_back( spell );
	set_sounds();
	change_active_spell_effect();

	// reset color if needed.
	int hue;
	int hue_delta;
	int saturation;
	int brightness;

	hue = m_hue_target;
	hue_delta = m_hue_delta;
	saturation = m_saturation;
	brightness = m_brightness;
	m_hue_target = 0;
	m_hue_delta = 0;
	m_saturation = 256;
	m_brightness = 256;
	set_color( hue, hue_delta, saturation, brightness );
	set_wait_animation();
	return true;
}

static bool write_abilities( std::streambuf& stream, bool const* ability )
{
	int i;
	int count = 0;

	for (i = 0; i < k_ability_count; ++i)
		if (ability[i])
			++count;
	put<t_uint16>( stream, count );
	for (i = 0; i < k_ability_count; ++i)
		if (ability[i])
			put<t_int16>( stream, i );
	return true;
}

bool t_combat_creature::write( t_combat_writer& writer ) const
{
	std::streambuf&			stream = writer.get_stream();
	t_battlefield const&	battlefield = writer.get_battlefield();

	put<t_uint16>( stream, k_current_version );
	if (!t_combat_actor::write( writer ))
		return false;
	if (!t_abstract_combat_creature::write( stream ))
		return false;

	int  army_slot_number = m_original_slot;
	bool army_side = belongs_to_defender();

	if (army_slot_number < 0 && get_hero() != 0)
	{
		// It's summoned.  Try and find source hero in armies.
		int side;
		int i;

		for (side = 0; side < 2; ++side)
		{
			army_side = side != 0;

			t_creature_array& army = battlefield.get_army( army_side );

			for (i = 0; i < t_creature_array::k_size; ++i)
				if (&army[i] == m_creature_stack.get())
					break;
			if (i < t_creature_array::k_size)
			{
				army_slot_number = i;
				break;
			}
		}
		assert( army_slot_number >= 0 );
	}	
	put<t_int8>( stream, army_slot_number );
	if (army_slot_number <  0)
	{	// create a creature stack
		put<t_int16>( stream, get_creature_type() );
		put<t_uint32>( stream, get_original_number() );
	}
	else
	{
		put<t_uint8>( stream, army_side );
	}

	writer.write( m_active_shadow );
	::write( stream, m_ai_retaliation_weight_reduction, sizeof( m_ai_retaliation_weight_reduction ));
	put<t_int16>( stream, m_artifact_move_bonus );
	put<t_int16>( stream, m_artifact_spell_cost_modifier );
	::write( stream, m_base_defense, sizeof( m_base_defense ));
	put<t_int16>( stream, m_block_chance );
	put<t_int16>( stream, m_default_spell );
	put<t_int8>( stream, m_defending );
	put<t_int16>( stream, m_defense_added );
	::write( stream, m_defense_bonus, sizeof( m_defense_bonus ));
	put<t_uint8>( stream, m_got_bad_luck );
	put<t_uint8>( stream, m_got_bad_morale );
	put<t_uint8>( stream, m_got_good_luck );
	put<t_uint8>( stream, m_got_good_morale	);
	put<t_uint8>( stream, m_has_leeched );
	put<t_uint8>( stream, m_has_regenerated	);
	put<t_int16>( stream, m_guardian_robe_strength );
	write_abilities( stream, m_has_ability );
	put<t_uint8>( stream, m_is_underlay	);
	writer.write( m_label );
	put<t_int16>( stream, m_luck_bonus );
	writer.write( m_magic_leech_client );
	put<t_int16>( stream, m_magic_resistance_bonus );
	put<t_int16>( stream, m_martyr_clients.size() );

	t_combat_creature_list::const_iterator index = m_martyr_clients.begin();

	for (; index != m_martyr_clients.end(); ++index)
	{
		writer.write( *index );
	}
	writer.write( m_martyr_protector );
	put<t_int16>( stream, m_morale );
	put<t_int16>( stream, m_morale_bonus );
	put<t_uint8>( stream, m_morale_roll	);
	put<t_uint8>( stream, m_morale_shown );
	put<t_int16>( stream, m_move_bonus );
	put<t_uint8>( stream, m_normal_alpha );
	write_abilities( stream, m_normally_has_ability );
	put<t_int16>( stream, m_offense_bonus );
	put<t_uint8>( stream, m_plague_checked );
	put<t_uint8>( stream, m_poison_checked );
	put<t_int8>( stream, m_preferred_action	);
	put<t_int32>( stream, m_retaliation_time_stamp );
	put<t_uint8>( stream, m_selected );
	writer.write( m_selection_shadow );
	put<t_int16>( stream, m_tactics_speed_bonus );
	put<t_uint8>( stream, m_waiting );
	put<bool>( stream, m_in_tower );
	return true;
}

void t_combat_creature::add_poison( int amount, t_combat_creature const & source )
{
	m_new_poison_source = &source;
	m_new_poison += amount;
}


static t_external_string const k_combat_action_pain_mirror_effect( "combat_action.pain_mirror_effect" );

// ------------------------------------------------------------------------------
// pain mirror action
// ------------------------------------------------------------------------------
t_pain_mirror_action::t_pain_mirror_action( t_combat_creature_ptr caster, 
										    t_combat_creature_ptr target,
			                                int damage, bool permanent )
					: m_battlefield( caster->get_battlefield() )
{
	m_caster = caster;
	m_target = target;
	m_damage = (target->modify_spell_damage( 0, damage, k_spell_pain_mirror ) + 1) / 2;
	m_permanent = permanent;
}

// ------------------------------------------------------------------------------
// pain mirror action
// ------------------------------------------------------------------------------
void t_pain_mirror_action::execute( t_window* )
{
	t_combat_action_message message( *m_caster, k_combat_action_pain_mirror_effect );

	m_target->add_damage( m_damage, 0, false, m_permanent, message );
	m_target->check_flinch( message );
	m_target->apply_damage( m_battlefield, true );
	m_battlefield.update_state();
}



// ------------------------------------------------------------------------------
// pain mirror action
// ------------------------------------------------------------------------------
void t_pain_mirror_action::operator()()
{
	t_battlefield& battlefield = m_battlefield;

	battlefield.resolve_attacks();
	if (m_target->get_number() == 0 || m_damage <= 0)
		return;

	t_combat_action_message message( *m_caster, k_combat_action_pain_mirror_effect );

	battlefield.start_animation( *m_caster, k_spell_pain_mirror, 
		                         new t_delay_effect( this ), message );
}

